eve-lark 0.4.3 → 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 +189 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -610,10 +610,29 @@ function buildStreamingCard(opts) {
|
|
|
610
610
|
lines.push(`<font color='grey'>${opts.status}</font>`);
|
|
611
611
|
}
|
|
612
612
|
lines.push(opts.buffer.length > 0 ? opts.buffer : "_\u2026_");
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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 };
|
|
617
636
|
}
|
|
618
637
|
__name(buildStreamingCard, "buildStreamingCard");
|
|
619
638
|
function buildErrorCard(message) {
|
|
@@ -681,10 +700,12 @@ function buildAskCard(request) {
|
|
|
681
700
|
return { config: { ...BASE_CONFIG }, elements };
|
|
682
701
|
}
|
|
683
702
|
__name(buildAskCard, "buildAskCard");
|
|
684
|
-
function buildAskAnsweredCard(request, selected) {
|
|
685
|
-
const elements = [
|
|
686
|
-
|
|
687
|
-
|
|
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 } });
|
|
688
709
|
const summary = selected.kind === "option" ? `<font color='green'>\u2713 ${escapeMarkdown(selected.label)}</font>` : `<font color='green'>\u2713 ${escapeMarkdown(selected.text)}</font>`;
|
|
689
710
|
elements.push({ tag: "div", text: { tag: "lark_md", content: summary } });
|
|
690
711
|
return { config: { ...BASE_CONFIG }, elements };
|
|
@@ -702,6 +723,28 @@ function buildCardKitStreamingCard(opts) {
|
|
|
702
723
|
lines.push(`<font color='grey'>${opts.status}</font>`);
|
|
703
724
|
}
|
|
704
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
|
+
}
|
|
705
748
|
return {
|
|
706
749
|
schema: "2.0",
|
|
707
750
|
config: {
|
|
@@ -709,27 +752,38 @@ function buildCardKitStreamingCard(opts) {
|
|
|
709
752
|
wide_screen_mode: true,
|
|
710
753
|
update_multi: true
|
|
711
754
|
},
|
|
712
|
-
body: {
|
|
713
|
-
elements: [
|
|
714
|
-
{ tag: "markdown", content: lines.join("\n\n") }
|
|
715
|
-
]
|
|
716
|
-
}
|
|
755
|
+
body: { elements }
|
|
717
756
|
};
|
|
718
757
|
}
|
|
719
758
|
__name(buildCardKitStreamingCard, "buildCardKitStreamingCard");
|
|
720
|
-
function buildCardKitFinalCard(text, toolCalls) {
|
|
759
|
+
function buildCardKitFinalCard(text, toolCalls, askRequest) {
|
|
721
760
|
const lines = [];
|
|
722
761
|
const toolLine = renderToolCalls(toolCalls ?? []);
|
|
723
762
|
if (toolLine) lines.push(toolLine);
|
|
724
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
|
+
}
|
|
725
783
|
return {
|
|
726
784
|
schema: "2.0",
|
|
727
785
|
config: { streaming_mode: false, wide_screen_mode: true, update_multi: true },
|
|
728
|
-
body: {
|
|
729
|
-
elements: [
|
|
730
|
-
{ tag: "markdown", content: lines.join("\n\n") }
|
|
731
|
-
]
|
|
732
|
-
}
|
|
786
|
+
body: { elements }
|
|
733
787
|
};
|
|
734
788
|
}
|
|
735
789
|
__name(buildCardKitFinalCard, "buildCardKitFinalCard");
|
|
@@ -746,6 +800,10 @@ var StreamingCardController = class {
|
|
|
746
800
|
status;
|
|
747
801
|
messageId;
|
|
748
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;
|
|
749
807
|
/** Tool calls made during this turn, in order. Rendered above the buffer
|
|
750
808
|
* so users can see what the agent is doing / has done, Claude-Code-style. */
|
|
751
809
|
toolCalls = [];
|
|
@@ -817,6 +875,69 @@ var StreamingCardController = class {
|
|
|
817
875
|
getToolCalls() {
|
|
818
876
|
return this.toolCalls;
|
|
819
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
|
+
}
|
|
820
941
|
async finalize(fullText) {
|
|
821
942
|
if (this.state === "completed" || this.state === "aborted") return;
|
|
822
943
|
this.cancelCreateTimer();
|
|
@@ -836,7 +957,7 @@ var StreamingCardController = class {
|
|
|
836
957
|
try {
|
|
837
958
|
const res = await this.client.sendCard({
|
|
838
959
|
chatId: this.deps.chatId,
|
|
839
|
-
card: this.deps.useCardKitV2 ? buildCardKitFinalCard(fullText, this.toolCalls) : buildTextCard(fullText),
|
|
960
|
+
card: this.deps.useCardKitV2 ? buildCardKitFinalCard(fullText, this.toolCalls, this.askRequest) : buildTextCard(fullText),
|
|
840
961
|
rootId: this.deps.rootId,
|
|
841
962
|
parentId: this.deps.parentId
|
|
842
963
|
});
|
|
@@ -862,7 +983,7 @@ var StreamingCardController = class {
|
|
|
862
983
|
}
|
|
863
984
|
await this.client.patchCard({
|
|
864
985
|
messageId: this.messageId,
|
|
865
|
-
card: this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: fullText, streamingMode: false, toolCalls: this.toolCalls }) : buildStreamingCard({ buffer: fullText, status: void 0, toolCalls: this.toolCalls })
|
|
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 })
|
|
866
987
|
});
|
|
867
988
|
this.state = "completed";
|
|
868
989
|
}
|
|
@@ -914,7 +1035,7 @@ var StreamingCardController = class {
|
|
|
914
1035
|
try {
|
|
915
1036
|
const res = await this.client.sendCard({
|
|
916
1037
|
chatId: this.deps.chatId,
|
|
917
|
-
card: this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls }) : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls }),
|
|
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 }),
|
|
918
1039
|
rootId: this.deps.rootId,
|
|
919
1040
|
parentId: this.deps.parentId
|
|
920
1041
|
});
|
|
@@ -952,7 +1073,7 @@ var StreamingCardController = class {
|
|
|
952
1073
|
if (this.state !== "streaming") return;
|
|
953
1074
|
if (this.patchInFlight) return;
|
|
954
1075
|
if (this.messageId === void 0) return;
|
|
955
|
-
const card = this.deps.useCardKitV2 ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls }) : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls });
|
|
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 });
|
|
956
1077
|
this.patchInFlight = this.client.patchCard({ messageId: this.messageId, card }).catch((e) => {
|
|
957
1078
|
console.warn(
|
|
958
1079
|
"[eve-lark] streaming card patch failed:",
|
|
@@ -1609,11 +1730,6 @@ function createLarkChannel(optionsInput) {
|
|
|
1609
1730
|
return ctrl;
|
|
1610
1731
|
}
|
|
1611
1732
|
__name(getController, "getController");
|
|
1612
|
-
function dropController(sessionId) {
|
|
1613
|
-
controllers.delete(sessionId);
|
|
1614
|
-
sessionMeta.delete(sessionId);
|
|
1615
|
-
}
|
|
1616
|
-
__name(dropController, "dropController");
|
|
1617
1733
|
async function cleanupAckReaction(sessionId) {
|
|
1618
1734
|
const meta = sessionMeta.get(sessionId);
|
|
1619
1735
|
if (!meta?.ackReactionId || !meta.messageId) return;
|
|
@@ -1983,14 +2099,18 @@ function createLarkChannel(optionsInput) {
|
|
|
1983
2099
|
helpers.waitUntil(
|
|
1984
2100
|
(async () => {
|
|
1985
2101
|
if (pending.cardMessageId && selectedOpt) {
|
|
2102
|
+
const ctrlForBuffer = controllers.get(pending.sessionId);
|
|
2103
|
+
const priorBuffer = ctrlForBuffer?.getBuffer() ?? void 0;
|
|
1986
2104
|
try {
|
|
1987
2105
|
await client.patchCard({
|
|
1988
2106
|
messageId: pending.cardMessageId,
|
|
1989
|
-
card: buildAskAnsweredCard(
|
|
1990
|
-
|
|
1991
|
-
label: selectedOpt.label
|
|
1992
|
-
|
|
2107
|
+
card: buildAskAnsweredCard(
|
|
2108
|
+
pending.request,
|
|
2109
|
+
{ kind: "option", label: selectedOpt.label },
|
|
2110
|
+
priorBuffer
|
|
2111
|
+
)
|
|
1993
2112
|
});
|
|
2113
|
+
ctrlForBuffer?.clearAskRequest();
|
|
1994
2114
|
} catch (e) {
|
|
1995
2115
|
console.warn(
|
|
1996
2116
|
"[eve-lark] patchCard after ask-answer failed:",
|
|
@@ -2101,22 +2221,29 @@ function createLarkChannel(optionsInput) {
|
|
|
2101
2221
|
`[eve-lark] input.requested sessionId=${sessionId} chatId=${info.chatId} count=${requests.length}`
|
|
2102
2222
|
);
|
|
2103
2223
|
for (const req of requests) {
|
|
2104
|
-
const
|
|
2224
|
+
const existingCtrl = controllers.get(sessionId);
|
|
2225
|
+
const canPatchExisting = existingCtrl && existingCtrl.getMessageId() && (options.replyMode === "streaming" || options.replyMode === "streaming-v2");
|
|
2105
2226
|
let cardMessageId;
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
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
|
+
}
|
|
2120
2247
|
}
|
|
2121
2248
|
const pending = {
|
|
2122
2249
|
requestId: req.requestId,
|
|
@@ -2155,7 +2282,6 @@ function createLarkChannel(optionsInput) {
|
|
|
2155
2282
|
await deliverReply(sessionId, info, text);
|
|
2156
2283
|
} finally {
|
|
2157
2284
|
await cleanupAckReaction(sessionId);
|
|
2158
|
-
dropController(sessionId);
|
|
2159
2285
|
}
|
|
2160
2286
|
},
|
|
2161
2287
|
async "turn.failed"(data, _channel, ctx) {
|
|
@@ -2196,7 +2322,6 @@ function createLarkChannel(optionsInput) {
|
|
|
2196
2322
|
}
|
|
2197
2323
|
}
|
|
2198
2324
|
await cleanupAckReaction(sessionId);
|
|
2199
|
-
dropController(sessionId);
|
|
2200
2325
|
},
|
|
2201
2326
|
async "session.failed"(data) {
|
|
2202
2327
|
const userText = formatFailureMessage(data, "session failed", { sentence: "session" });
|
|
@@ -2205,11 +2330,23 @@ function createLarkChannel(optionsInput) {
|
|
|
2205
2330
|
`[eve-lark] session.failed: ${userText}` + (errorId ? ` (errorId=${errorId})` : "")
|
|
2206
2331
|
);
|
|
2207
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
|
+
},
|
|
2208
2346
|
// Turn ended cleanly. eve fires this after the final message.completed
|
|
2209
2347
|
// (or instead of it when the assistant step ended in tool-calls with no
|
|
2210
|
-
// visible text).
|
|
2211
|
-
//
|
|
2212
|
-
// 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).
|
|
2213
2350
|
async "turn.completed"(data, _channel, ctx) {
|
|
2214
2351
|
const sessionId = ctx?.session?.id;
|
|
2215
2352
|
if (!sessionId) return;
|
|
@@ -2217,7 +2354,6 @@ function createLarkChannel(optionsInput) {
|
|
|
2217
2354
|
await cleanupAckReaction(sessionId);
|
|
2218
2355
|
} catch {
|
|
2219
2356
|
}
|
|
2220
|
-
dropController(sessionId);
|
|
2221
2357
|
},
|
|
2222
2358
|
// The agent needs the user to sign in to an external service (e.g.
|
|
2223
2359
|
// GitHub, Slack, Linear). Render a card with a "Sign in with <X>"
|