@vibe-lark/larkpal 0.1.19 → 0.1.20
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/main.mjs +175 -38
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -6973,6 +6973,13 @@ const THROTTLE_CONSTANTS = {
|
|
|
6973
6973
|
* mutex-guarded flushing, and reflush-on-conflict. Contains no
|
|
6974
6974
|
* business logic — the actual flush work is provided via a callback.
|
|
6975
6975
|
*/
|
|
6976
|
+
/** flush 超时保护:单次 flush API 调用最大允许时间(超过后释放锁,允许后续 flush 继续) */
|
|
6977
|
+
const FLUSH_TIMEOUT_MS = 15e3;
|
|
6978
|
+
var FlushTimeoutError = class extends Error {
|
|
6979
|
+
constructor() {
|
|
6980
|
+
super("flush timeout");
|
|
6981
|
+
}
|
|
6982
|
+
};
|
|
6976
6983
|
var FlushController = class {
|
|
6977
6984
|
flushInProgress = false;
|
|
6978
6985
|
flushResolvers = [];
|
|
@@ -7004,6 +7011,9 @@ var FlushController = class {
|
|
|
7004
7011
|
*
|
|
7005
7012
|
* If a flush is already in progress, marks needsReflush so a
|
|
7006
7013
|
* follow-up flush fires immediately after the current one completes.
|
|
7014
|
+
*
|
|
7015
|
+
* 包含超时保护:如果单次 flush 超过 FLUSH_TIMEOUT_MS,强制释放锁并触发 reflush,
|
|
7016
|
+
* 防止飞书 API 偶发慢响应导致卡片长时间无法更新。
|
|
7007
7017
|
*/
|
|
7008
7018
|
async flush() {
|
|
7009
7019
|
if (!this.cardMessageReady() || this.flushInProgress || this.isCompleted) {
|
|
@@ -7014,8 +7024,19 @@ var FlushController = class {
|
|
|
7014
7024
|
this.needsReflush = false;
|
|
7015
7025
|
this.lastUpdateTime = Date.now();
|
|
7016
7026
|
try {
|
|
7017
|
-
|
|
7027
|
+
let timeoutTimer;
|
|
7028
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
7029
|
+
timeoutTimer = setTimeout(() => reject(new FlushTimeoutError()), FLUSH_TIMEOUT_MS);
|
|
7030
|
+
});
|
|
7031
|
+
try {
|
|
7032
|
+
await Promise.race([this.doFlush(), timeoutPromise]);
|
|
7033
|
+
} finally {
|
|
7034
|
+
clearTimeout(timeoutTimer);
|
|
7035
|
+
}
|
|
7018
7036
|
this.lastUpdateTime = Date.now();
|
|
7037
|
+
} catch (err) {
|
|
7038
|
+
if (!(err instanceof FlushTimeoutError)) throw err;
|
|
7039
|
+
this.needsReflush = true;
|
|
7019
7040
|
} finally {
|
|
7020
7041
|
this.flushInProgress = false;
|
|
7021
7042
|
const resolvers = this.flushResolvers;
|
|
@@ -7409,6 +7430,21 @@ var UnavailableGuard = class {
|
|
|
7409
7430
|
}
|
|
7410
7431
|
};
|
|
7411
7432
|
//#endregion
|
|
7433
|
+
//#region src/card/active-card-store.ts
|
|
7434
|
+
const store = /* @__PURE__ */ new Map();
|
|
7435
|
+
/** 注册活跃的 card controller(每条消息调度时) */
|
|
7436
|
+
function registerActiveCard(sessionId, controller) {
|
|
7437
|
+
store.set(sessionId, controller);
|
|
7438
|
+
}
|
|
7439
|
+
/** 注销(controller 进入终态后调用) */
|
|
7440
|
+
function unregisterActiveCard(sessionId) {
|
|
7441
|
+
store.delete(sessionId);
|
|
7442
|
+
}
|
|
7443
|
+
/** 获取当前活跃的 card controller */
|
|
7444
|
+
function getActiveCard(sessionId) {
|
|
7445
|
+
return store.get(sessionId);
|
|
7446
|
+
}
|
|
7447
|
+
//#endregion
|
|
7412
7448
|
//#region src/card/streaming-card-controller.ts
|
|
7413
7449
|
/**
|
|
7414
7450
|
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
@@ -7712,6 +7748,7 @@ var StreamingCardController = class StreamingCardController {
|
|
|
7712
7748
|
this.flush.complete();
|
|
7713
7749
|
this.disposeShutdownHook?.();
|
|
7714
7750
|
this.disposeShutdownHook = null;
|
|
7751
|
+
unregisterActiveCard(this.deps.sessionKey);
|
|
7715
7752
|
if (this.phase === "terminated" || this.phase === "creation_failed") clearToolUseTraceRun(this.deps.sessionKey);
|
|
7716
7753
|
}
|
|
7717
7754
|
markToolUseActivity() {
|
|
@@ -7889,21 +7926,6 @@ var StreamingCardController = class StreamingCardController {
|
|
|
7889
7926
|
const idleEffectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
|
|
7890
7927
|
try {
|
|
7891
7928
|
if (this.cardKit.cardMessageId) {
|
|
7892
|
-
if (idleEffectiveCardId) {
|
|
7893
|
-
const seqBeforeClose = this.cardKit.cardKitSequence;
|
|
7894
|
-
this.cardKit.cardKitSequence += 1;
|
|
7895
|
-
log$13.info("onIdle: closing streaming mode", {
|
|
7896
|
-
seqBefore: seqBeforeClose,
|
|
7897
|
-
seqAfter: this.cardKit.cardKitSequence
|
|
7898
|
-
});
|
|
7899
|
-
await setCardStreamingMode({
|
|
7900
|
-
cfg: this.deps.cfg,
|
|
7901
|
-
cardId: idleEffectiveCardId,
|
|
7902
|
-
streamingMode: false,
|
|
7903
|
-
sequence: this.cardKit.cardKitSequence,
|
|
7904
|
-
accountId: this.deps.accountId
|
|
7905
|
-
});
|
|
7906
|
-
}
|
|
7907
7929
|
const isNoReplyLeak = !this.text.completedText && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());
|
|
7908
7930
|
const displayText = this.text.completedText || (isNoReplyLeak ? "" : this.text.accumulatedText) || "Done.";
|
|
7909
7931
|
if (!this.text.completedText && !this.text.accumulatedText) log$13.warn("reply completed without visible text, using empty-reply fallback");
|
|
@@ -7941,33 +7963,65 @@ var StreamingCardController = class StreamingCardController {
|
|
|
7941
7963
|
isAborted: this._userAborted,
|
|
7942
7964
|
stepTitles: idleToolUseDisplay?.steps?.map((s) => s.title)
|
|
7943
7965
|
});
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7966
|
+
const MAX_FINAL_UPDATE_RETRIES = 3;
|
|
7967
|
+
const FINAL_UPDATE_RETRY_DELAY_MS = 2e3;
|
|
7968
|
+
for (let attempt = 1; attempt <= MAX_FINAL_UPDATE_RETRIES; attempt++) try {
|
|
7969
|
+
if (idleEffectiveCardId) {
|
|
7970
|
+
const seqBeforeClose = this.cardKit.cardKitSequence;
|
|
7971
|
+
this.cardKit.cardKitSequence += 1;
|
|
7972
|
+
log$13.info("onIdle: closing streaming mode", {
|
|
7973
|
+
attempt,
|
|
7974
|
+
seqBefore: seqBeforeClose,
|
|
7975
|
+
seqAfter: this.cardKit.cardKitSequence
|
|
7976
|
+
});
|
|
7977
|
+
await setCardStreamingMode({
|
|
7978
|
+
cfg: this.deps.cfg,
|
|
7979
|
+
cardId: idleEffectiveCardId,
|
|
7980
|
+
streamingMode: false,
|
|
7981
|
+
sequence: this.cardKit.cardKitSequence,
|
|
7982
|
+
accountId: this.deps.accountId
|
|
7983
|
+
});
|
|
7984
|
+
const seqBeforeUpdate = this.cardKit.cardKitSequence;
|
|
7985
|
+
this.cardKit.cardKitSequence += 1;
|
|
7986
|
+
log$13.info("onIdle: updating final card", {
|
|
7987
|
+
attempt,
|
|
7988
|
+
seqBefore: seqBeforeUpdate,
|
|
7989
|
+
seqAfter: this.cardKit.cardKitSequence
|
|
7990
|
+
});
|
|
7991
|
+
await updateCardKitCard({
|
|
7992
|
+
cfg: this.deps.cfg,
|
|
7993
|
+
cardId: idleEffectiveCardId,
|
|
7994
|
+
card: toCardKit2(completeCard),
|
|
7995
|
+
sequence: this.cardKit.cardKitSequence,
|
|
7996
|
+
accountId: this.deps.accountId
|
|
7997
|
+
});
|
|
7998
|
+
} else await updateCardFeishu({
|
|
7952
7999
|
cfg: this.deps.cfg,
|
|
7953
|
-
|
|
7954
|
-
card:
|
|
7955
|
-
sequence: this.cardKit.cardKitSequence,
|
|
8000
|
+
messageId: this.cardKit.cardMessageId,
|
|
8001
|
+
card: completeCard,
|
|
7956
8002
|
accountId: this.deps.accountId
|
|
7957
8003
|
});
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
8004
|
+
log$13.info("reply completed, card finalized", {
|
|
8005
|
+
elapsedMs: this.elapsed(),
|
|
8006
|
+
isCardKit: !!idleEffectiveCardId,
|
|
8007
|
+
attempt
|
|
8008
|
+
});
|
|
8009
|
+
break;
|
|
8010
|
+
} catch (retryErr) {
|
|
8011
|
+
log$13.warn("final card update attempt failed", {
|
|
8012
|
+
attempt,
|
|
8013
|
+
maxRetries: MAX_FINAL_UPDATE_RETRIES,
|
|
8014
|
+
error: String(retryErr)
|
|
8015
|
+
});
|
|
8016
|
+
if (attempt < MAX_FINAL_UPDATE_RETRIES) await new Promise((resolve) => setTimeout(resolve, FINAL_UPDATE_RETRY_DELAY_MS * attempt));
|
|
8017
|
+
else log$13.error("final card update exhausted all retries, card may remain in streaming state", {
|
|
8018
|
+
cardId: idleEffectiveCardId,
|
|
8019
|
+
messageId: this.cardKit.cardMessageId
|
|
8020
|
+
});
|
|
8021
|
+
}
|
|
7968
8022
|
}
|
|
7969
8023
|
} catch (err) {
|
|
7970
|
-
log$13.
|
|
8024
|
+
log$13.error("final card update failed (outer)", { error: String(err) });
|
|
7971
8025
|
} finally {
|
|
7972
8026
|
clearToolUseTraceRun(this.deps.sessionKey);
|
|
7973
8027
|
}
|
|
@@ -8041,6 +8095,78 @@ var StreamingCardController = class StreamingCardController {
|
|
|
8041
8095
|
clearToolUseTraceRun(this.deps.sessionKey);
|
|
8042
8096
|
}
|
|
8043
8097
|
}
|
|
8098
|
+
/**
|
|
8099
|
+
* 强制关闭 streaming 状态(兜底方法)。
|
|
8100
|
+
*
|
|
8101
|
+
* 当进程已退出且卡片可能因 API 调用失败仍停留在 streaming 状态时,
|
|
8102
|
+
* 由 session-control-handler 调用,强制调用 closeStreaming + updateCard。
|
|
8103
|
+
* 即使已经是 terminal phase 也会尝试执行 API 调用。
|
|
8104
|
+
*/
|
|
8105
|
+
async forceCloseStreaming() {
|
|
8106
|
+
const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
|
|
8107
|
+
if (!effectiveCardId && !this.cardKit.cardMessageId) {
|
|
8108
|
+
log$13.warn("forceCloseStreaming: no card to close");
|
|
8109
|
+
return;
|
|
8110
|
+
}
|
|
8111
|
+
log$13.info("forceCloseStreaming: 强制终态化卡片", {
|
|
8112
|
+
cardId: effectiveCardId,
|
|
8113
|
+
messageId: this.cardKit.cardMessageId,
|
|
8114
|
+
phase: this.phase
|
|
8115
|
+
});
|
|
8116
|
+
this._userAborted = true;
|
|
8117
|
+
try {
|
|
8118
|
+
const displayText = this.text.accumulatedText || this.text.completedText || "Done.";
|
|
8119
|
+
const idleToolUseDisplay = this.computeToolUseDisplay();
|
|
8120
|
+
const terminalContent = prepareTerminalCardContent({
|
|
8121
|
+
text: displayText,
|
|
8122
|
+
reasoningText: this.reasoning.accumulatedReasoningText || void 0
|
|
8123
|
+
}, this.imageResolver);
|
|
8124
|
+
const completeCard = buildCardContent("complete", {
|
|
8125
|
+
text: terminalContent.text,
|
|
8126
|
+
reasoningText: terminalContent.reasoningText,
|
|
8127
|
+
reasoningElapsedMs: this.reasoning.reasoningElapsedMs || void 0,
|
|
8128
|
+
toolUseSteps: idleToolUseDisplay?.steps,
|
|
8129
|
+
toolUseTitleSuffix: this.computeToolUseTitleSuffix(idleToolUseDisplay),
|
|
8130
|
+
toolUseElapsedMs: this.visibleToolUseElapsedMs,
|
|
8131
|
+
showToolUse: this.deps.toolUseDisplay.showToolUse,
|
|
8132
|
+
elapsedMs: this.elapsed(),
|
|
8133
|
+
isAborted: true,
|
|
8134
|
+
footer: this.deps.resolvedFooter,
|
|
8135
|
+
sessionId: this.deps.sessionKey,
|
|
8136
|
+
showUndoButton: false
|
|
8137
|
+
});
|
|
8138
|
+
if (effectiveCardId) {
|
|
8139
|
+
this.cardKit.cardKitSequence += 1;
|
|
8140
|
+
await setCardStreamingMode({
|
|
8141
|
+
cfg: this.deps.cfg,
|
|
8142
|
+
cardId: effectiveCardId,
|
|
8143
|
+
streamingMode: false,
|
|
8144
|
+
sequence: this.cardKit.cardKitSequence,
|
|
8145
|
+
accountId: this.deps.accountId
|
|
8146
|
+
});
|
|
8147
|
+
this.cardKit.cardKitSequence += 1;
|
|
8148
|
+
await updateCardKitCard({
|
|
8149
|
+
cfg: this.deps.cfg,
|
|
8150
|
+
cardId: effectiveCardId,
|
|
8151
|
+
card: toCardKit2(completeCard),
|
|
8152
|
+
sequence: this.cardKit.cardKitSequence,
|
|
8153
|
+
accountId: this.deps.accountId
|
|
8154
|
+
});
|
|
8155
|
+
} else if (this.cardKit.cardMessageId) await updateCardFeishu({
|
|
8156
|
+
cfg: this.deps.cfg,
|
|
8157
|
+
messageId: this.cardKit.cardMessageId,
|
|
8158
|
+
card: completeCard,
|
|
8159
|
+
accountId: this.deps.accountId
|
|
8160
|
+
});
|
|
8161
|
+
log$13.info("forceCloseStreaming: 卡片已强制终态化");
|
|
8162
|
+
if (!this.isTerminalPhase) this.finalizeCard("forceCloseStreaming", "abort");
|
|
8163
|
+
} catch (err) {
|
|
8164
|
+
log$13.error("forceCloseStreaming failed", { error: String(err) });
|
|
8165
|
+
} finally {
|
|
8166
|
+
unregisterActiveCard(this.deps.sessionKey);
|
|
8167
|
+
clearToolUseTraceRun(this.deps.sessionKey);
|
|
8168
|
+
}
|
|
8169
|
+
}
|
|
8044
8170
|
async ensureCardCreated() {
|
|
8045
8171
|
if (this.guard.shouldSkip("ensureCardCreated.precheck")) return;
|
|
8046
8172
|
if (this.cardKit.cardMessageId || this.phase === "creation_failed" || this.isTerminalPhase) return;
|
|
@@ -8805,6 +8931,7 @@ async function dispatchToCC(params) {
|
|
|
8805
8931
|
placeholderMessageId: params.placeholderMessageId
|
|
8806
8932
|
};
|
|
8807
8933
|
const cardController = new StreamingCardController(cardDeps);
|
|
8934
|
+
registerActiveCard(route.sessionId, cardController);
|
|
8808
8935
|
log$11.info("StreamingCardController 已创建", {
|
|
8809
8936
|
sessionId: route.sessionId,
|
|
8810
8937
|
chatId,
|
|
@@ -8987,6 +9114,7 @@ async function dispatchTeammateEval(params) {
|
|
|
8987
9114
|
model: false
|
|
8988
9115
|
}
|
|
8989
9116
|
});
|
|
9117
|
+
registerActiveCard(teammateSessionKey, cardController);
|
|
8990
9118
|
bridge = new CCStreamBridge(cardController, {
|
|
8991
9119
|
autoCompleteOnTurnEnd: true,
|
|
8992
9120
|
sessionKey: teammateSessionKey
|
|
@@ -11884,6 +12012,15 @@ async function handleAbortSession(params) {
|
|
|
11884
12012
|
sessionId,
|
|
11885
12013
|
status: proc?.status
|
|
11886
12014
|
});
|
|
12015
|
+
const activeCard = getActiveCard(sessionId);
|
|
12016
|
+
if (activeCard) {
|
|
12017
|
+
log.info("abort_session: 进程已退出但卡片仍在活跃状态,强制终态化", { sessionId });
|
|
12018
|
+
activeCard.forceCloseStreaming();
|
|
12019
|
+
return { toast: {
|
|
12020
|
+
type: "success",
|
|
12021
|
+
content: "任务已结束,正在更新卡片"
|
|
12022
|
+
} };
|
|
12023
|
+
}
|
|
11887
12024
|
return { toast: {
|
|
11888
12025
|
type: "info",
|
|
11889
12026
|
content: "当前没有正在执行的任务"
|