codex-to-im 1.0.34 → 1.0.36
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/daemon.mjs +292 -48
- package/package.json +1 -1
package/dist/daemon.mjs
CHANGED
|
@@ -5496,25 +5496,16 @@ function formatElapsed(ms) {
|
|
|
5496
5496
|
const remSec = Math.floor(sec % 60);
|
|
5497
5497
|
return `${min}m ${remSec}s`;
|
|
5498
5498
|
}
|
|
5499
|
-
function
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
${toolMd}` : toolMd;
|
|
5506
|
-
}
|
|
5507
|
-
return content || "\u{1F4AD} Thinking...";
|
|
5499
|
+
function buildStreamingTextContent(text2) {
|
|
5500
|
+
return text2 || "\u{1F4AD} Thinking...";
|
|
5501
|
+
}
|
|
5502
|
+
function buildStreamingToolsContent(tools) {
|
|
5503
|
+
return buildToolProgressMarkdown(tools);
|
|
5508
5504
|
}
|
|
5509
5505
|
function buildFinalCardJson(text2, tools, footer) {
|
|
5510
5506
|
const elements = [];
|
|
5511
|
-
|
|
5507
|
+
const content = preprocessFeishuMarkdown(text2);
|
|
5512
5508
|
const toolMd = buildToolProgressMarkdown(tools);
|
|
5513
|
-
if (toolMd) {
|
|
5514
|
-
content = content ? `${content}
|
|
5515
|
-
|
|
5516
|
-
${toolMd}` : toolMd;
|
|
5517
|
-
}
|
|
5518
5509
|
if (content) {
|
|
5519
5510
|
elements.push({
|
|
5520
5511
|
tag: "markdown",
|
|
@@ -5523,12 +5514,25 @@ ${toolMd}` : toolMd;
|
|
|
5523
5514
|
text_size: "normal"
|
|
5524
5515
|
});
|
|
5525
5516
|
}
|
|
5517
|
+
if (toolMd) {
|
|
5518
|
+
if (elements.length > 0) {
|
|
5519
|
+
elements.push({ tag: "hr" });
|
|
5520
|
+
}
|
|
5521
|
+
elements.push({
|
|
5522
|
+
tag: "markdown",
|
|
5523
|
+
content: toolMd,
|
|
5524
|
+
text_align: "left",
|
|
5525
|
+
text_size: "normal"
|
|
5526
|
+
});
|
|
5527
|
+
}
|
|
5526
5528
|
if (footer) {
|
|
5527
5529
|
const parts = [];
|
|
5528
5530
|
if (footer.status) parts.push(footer.status);
|
|
5529
5531
|
if (footer.elapsed) parts.push(footer.elapsed);
|
|
5530
5532
|
if (parts.length > 0) {
|
|
5531
|
-
elements.
|
|
5533
|
+
if (elements.length > 0) {
|
|
5534
|
+
elements.push({ tag: "hr" });
|
|
5535
|
+
}
|
|
5532
5536
|
elements.push({
|
|
5533
5537
|
tag: "markdown",
|
|
5534
5538
|
content: parts.join(" \xB7 "),
|
|
@@ -5595,6 +5599,8 @@ var DEDUP_MAX = 1e3;
|
|
|
5595
5599
|
var MAX_FILE_SIZE = 20 * 1024 * 1024;
|
|
5596
5600
|
var TYPING_EMOJI = "Typing";
|
|
5597
5601
|
var CARD_THROTTLE_MS = 200;
|
|
5602
|
+
var INITIAL_STREAMING_STATUS = "\u5DF2\u8FD0\u884C 0s";
|
|
5603
|
+
var EMPTY_STREAMING_TOOLS = "";
|
|
5598
5604
|
var MIME_BY_TYPE = {
|
|
5599
5605
|
image: "image/png",
|
|
5600
5606
|
file: "application/octet-stream",
|
|
@@ -5642,6 +5648,9 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5642
5648
|
isStreamingEnabled() {
|
|
5643
5649
|
return this.channelConfig.streamingEnabled !== false;
|
|
5644
5650
|
}
|
|
5651
|
+
supportsStructuredStreamingUi(_chatId) {
|
|
5652
|
+
return this.isStreamingEnabled();
|
|
5653
|
+
}
|
|
5645
5654
|
resolveStreamKey(chatId, streamKey) {
|
|
5646
5655
|
return streamKey?.trim() || chatId;
|
|
5647
5656
|
}
|
|
@@ -5840,13 +5849,29 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5840
5849
|
summary: { content: "\u601D\u8003\u4E2D..." }
|
|
5841
5850
|
},
|
|
5842
5851
|
body: {
|
|
5843
|
-
elements: [
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5852
|
+
elements: [
|
|
5853
|
+
{
|
|
5854
|
+
tag: "markdown",
|
|
5855
|
+
content: "\u{1F4AD} Thinking...",
|
|
5856
|
+
text_align: "left",
|
|
5857
|
+
text_size: "normal",
|
|
5858
|
+
element_id: "streaming_content"
|
|
5859
|
+
},
|
|
5860
|
+
{
|
|
5861
|
+
tag: "markdown",
|
|
5862
|
+
content: EMPTY_STREAMING_TOOLS,
|
|
5863
|
+
text_align: "left",
|
|
5864
|
+
text_size: "normal",
|
|
5865
|
+
element_id: "streaming_tools"
|
|
5866
|
+
},
|
|
5867
|
+
{
|
|
5868
|
+
tag: "markdown",
|
|
5869
|
+
content: INITIAL_STREAMING_STATUS,
|
|
5870
|
+
text_align: "left",
|
|
5871
|
+
text_size: "notation",
|
|
5872
|
+
element_id: "streaming_status"
|
|
5873
|
+
}
|
|
5874
|
+
]
|
|
5850
5875
|
}
|
|
5851
5876
|
};
|
|
5852
5877
|
const createResp = await cardkit.card.create({
|
|
@@ -5888,8 +5913,14 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5888
5913
|
toolCalls: [],
|
|
5889
5914
|
thinking: true,
|
|
5890
5915
|
pendingText: null,
|
|
5916
|
+
pendingStatusText: INITIAL_STREAMING_STATUS,
|
|
5917
|
+
renderedText: "\u{1F4AD} Thinking...",
|
|
5918
|
+
renderedToolsText: EMPTY_STREAMING_TOOLS,
|
|
5919
|
+
renderedStatusText: INITIAL_STREAMING_STATUS,
|
|
5891
5920
|
lastUpdateAt: 0,
|
|
5892
|
-
throttleTimer: null
|
|
5921
|
+
throttleTimer: null,
|
|
5922
|
+
flushInFlight: null,
|
|
5923
|
+
flushQueued: false
|
|
5893
5924
|
});
|
|
5894
5925
|
console.log(`[feishu-adapter] Streaming card created: streamKey=${cardKey}, cardId=${cardId}, msgId=${messageId}`);
|
|
5895
5926
|
return true;
|
|
@@ -5909,12 +5940,43 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5909
5940
|
state.thinking = false;
|
|
5910
5941
|
}
|
|
5911
5942
|
state.pendingText = text2;
|
|
5943
|
+
this.scheduleCardFlush(cardKey);
|
|
5944
|
+
}
|
|
5945
|
+
updateCardStatus(chatId, statusText, streamKey) {
|
|
5946
|
+
const cardKey = this.resolveStreamKey(chatId, streamKey);
|
|
5947
|
+
const state = this.activeCards.get(cardKey);
|
|
5948
|
+
if (!state || !this.restClient) return;
|
|
5949
|
+
state.pendingStatusText = statusText || INITIAL_STREAMING_STATUS;
|
|
5950
|
+
this.scheduleCardFlush(cardKey);
|
|
5951
|
+
}
|
|
5952
|
+
enqueueCardFlush(streamKey) {
|
|
5953
|
+
const state = this.activeCards.get(streamKey);
|
|
5954
|
+
if (!state) return;
|
|
5955
|
+
if (state.flushInFlight) {
|
|
5956
|
+
state.flushQueued = true;
|
|
5957
|
+
return;
|
|
5958
|
+
}
|
|
5959
|
+
state.flushInFlight = this.flushCardUpdate(streamKey).catch((err) => {
|
|
5960
|
+
console.warn("[feishu-adapter] cardElement.content failed:", err instanceof Error ? err.message : err);
|
|
5961
|
+
}).finally(() => {
|
|
5962
|
+
const current = this.activeCards.get(streamKey);
|
|
5963
|
+
if (!current) return;
|
|
5964
|
+
current.flushInFlight = null;
|
|
5965
|
+
if (current.flushQueued) {
|
|
5966
|
+
current.flushQueued = false;
|
|
5967
|
+
this.enqueueCardFlush(streamKey);
|
|
5968
|
+
}
|
|
5969
|
+
});
|
|
5970
|
+
}
|
|
5971
|
+
scheduleCardFlush(streamKey) {
|
|
5972
|
+
const state = this.activeCards.get(streamKey);
|
|
5973
|
+
if (!state) return;
|
|
5912
5974
|
const elapsed = Date.now() - state.lastUpdateAt;
|
|
5913
5975
|
if (elapsed < CARD_THROTTLE_MS && state.lastUpdateAt > 0) {
|
|
5914
5976
|
if (!state.throttleTimer) {
|
|
5915
5977
|
state.throttleTimer = setTimeout(() => {
|
|
5916
5978
|
state.throttleTimer = null;
|
|
5917
|
-
this.
|
|
5979
|
+
this.enqueueCardFlush(streamKey);
|
|
5918
5980
|
}, CARD_THROTTLE_MS - elapsed);
|
|
5919
5981
|
}
|
|
5920
5982
|
return;
|
|
@@ -5923,28 +5985,65 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5923
5985
|
clearTimeout(state.throttleTimer);
|
|
5924
5986
|
state.throttleTimer = null;
|
|
5925
5987
|
}
|
|
5926
|
-
this.
|
|
5988
|
+
this.enqueueCardFlush(streamKey);
|
|
5927
5989
|
}
|
|
5928
5990
|
/**
|
|
5929
5991
|
* Flush pending card update to Feishu API.
|
|
5930
5992
|
*/
|
|
5931
|
-
flushCardUpdate(streamKey) {
|
|
5993
|
+
async flushCardUpdate(streamKey) {
|
|
5932
5994
|
const state = this.activeCards.get(streamKey);
|
|
5933
5995
|
if (!state || !this.restClient) return;
|
|
5934
5996
|
const cardkit = this.restClient.cardkit?.v1;
|
|
5935
5997
|
if (!cardkit?.cardElement?.content) return;
|
|
5936
|
-
const content =
|
|
5937
|
-
state.
|
|
5938
|
-
const
|
|
5998
|
+
const content = buildStreamingTextContent(state.pendingText || "");
|
|
5999
|
+
const toolsText = buildStreamingToolsContent(state.toolCalls) || EMPTY_STREAMING_TOOLS;
|
|
6000
|
+
const statusText = state.pendingStatusText || INITIAL_STREAMING_STATUS;
|
|
6001
|
+
const updates = [];
|
|
6002
|
+
if (content !== state.renderedText) {
|
|
6003
|
+
updates.push({
|
|
6004
|
+
elementId: "streaming_content",
|
|
6005
|
+
content,
|
|
6006
|
+
onSuccess: () => {
|
|
6007
|
+
state.renderedText = content;
|
|
6008
|
+
}
|
|
6009
|
+
});
|
|
6010
|
+
}
|
|
6011
|
+
if (toolsText !== state.renderedToolsText) {
|
|
6012
|
+
updates.push({
|
|
6013
|
+
elementId: "streaming_tools",
|
|
6014
|
+
content: toolsText,
|
|
6015
|
+
onSuccess: () => {
|
|
6016
|
+
state.renderedToolsText = toolsText;
|
|
6017
|
+
}
|
|
6018
|
+
});
|
|
6019
|
+
}
|
|
6020
|
+
if (statusText !== state.renderedStatusText) {
|
|
6021
|
+
updates.push({
|
|
6022
|
+
elementId: "streaming_status",
|
|
6023
|
+
content: statusText,
|
|
6024
|
+
onSuccess: () => {
|
|
6025
|
+
state.renderedStatusText = statusText;
|
|
6026
|
+
}
|
|
6027
|
+
});
|
|
6028
|
+
}
|
|
6029
|
+
if (updates.length === 0) return;
|
|
5939
6030
|
const cardId = state.cardId;
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
6031
|
+
for (const update of updates) {
|
|
6032
|
+
state.sequence++;
|
|
6033
|
+
try {
|
|
6034
|
+
await cardkit.cardElement.content({
|
|
6035
|
+
path: { card_id: cardId, element_id: update.elementId },
|
|
6036
|
+
data: { content: update.content, sequence: state.sequence }
|
|
6037
|
+
});
|
|
6038
|
+
update.onSuccess();
|
|
6039
|
+
state.lastUpdateAt = Date.now();
|
|
6040
|
+
} catch (err) {
|
|
6041
|
+
console.warn(
|
|
6042
|
+
`[feishu-adapter] cardElement.content failed for ${update.elementId}:`,
|
|
6043
|
+
err instanceof Error ? err.message : err
|
|
6044
|
+
);
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
5948
6047
|
}
|
|
5949
6048
|
/**
|
|
5950
6049
|
* Update tool progress in the streaming card.
|
|
@@ -5954,7 +6053,27 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5954
6053
|
const state = this.activeCards.get(cardKey);
|
|
5955
6054
|
if (!state) return;
|
|
5956
6055
|
state.toolCalls = tools;
|
|
5957
|
-
this.
|
|
6056
|
+
this.scheduleCardFlush(cardKey);
|
|
6057
|
+
}
|
|
6058
|
+
async awaitCardFlushCompletion(streamKey) {
|
|
6059
|
+
while (true) {
|
|
6060
|
+
const state = this.activeCards.get(streamKey);
|
|
6061
|
+
if (!state) return;
|
|
6062
|
+
const inFlight = state.flushInFlight;
|
|
6063
|
+
if (inFlight) {
|
|
6064
|
+
try {
|
|
6065
|
+
await inFlight;
|
|
6066
|
+
} catch {
|
|
6067
|
+
}
|
|
6068
|
+
continue;
|
|
6069
|
+
}
|
|
6070
|
+
if (state.flushQueued) {
|
|
6071
|
+
state.flushQueued = false;
|
|
6072
|
+
this.enqueueCardFlush(streamKey);
|
|
6073
|
+
continue;
|
|
6074
|
+
}
|
|
6075
|
+
return;
|
|
6076
|
+
}
|
|
5958
6077
|
}
|
|
5959
6078
|
/**
|
|
5960
6079
|
* Finalize the streaming card: close streaming mode, update with final content + footer.
|
|
@@ -5976,6 +6095,7 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5976
6095
|
clearTimeout(state.throttleTimer);
|
|
5977
6096
|
state.throttleTimer = null;
|
|
5978
6097
|
}
|
|
6098
|
+
await this.awaitCardFlushCompletion(cardKey);
|
|
5979
6099
|
try {
|
|
5980
6100
|
state.sequence++;
|
|
5981
6101
|
await cardkit.card.settings({
|
|
@@ -6041,6 +6161,9 @@ ${trimmedResponse}`;
|
|
|
6041
6161
|
hasActiveCard(chatId, streamKey) {
|
|
6042
6162
|
return this.activeCards.has(this.resolveStreamKey(chatId, streamKey));
|
|
6043
6163
|
}
|
|
6164
|
+
hasActiveStreamingUi(chatId, streamKey) {
|
|
6165
|
+
return this.hasActiveCard(chatId, streamKey);
|
|
6166
|
+
}
|
|
6044
6167
|
// ── Streaming adapter interface ────────────────────────────────
|
|
6045
6168
|
/**
|
|
6046
6169
|
* Called by bridge-manager on each text SSE event.
|
|
@@ -6070,6 +6193,19 @@ ${trimmedResponse}`;
|
|
|
6070
6193
|
if (!this.isStreamingEnabled()) return;
|
|
6071
6194
|
this.updateToolProgress(chatId, tools, streamKey);
|
|
6072
6195
|
}
|
|
6196
|
+
onStreamStatus(chatId, statusText, streamKey) {
|
|
6197
|
+
if (!this.isStreamingEnabled()) return;
|
|
6198
|
+
const cardKey = this.resolveStreamKey(chatId, streamKey);
|
|
6199
|
+
if (!this.activeCards.has(cardKey)) {
|
|
6200
|
+
const messageId = this.lastIncomingMessageId.get(chatId);
|
|
6201
|
+
this.createStreamingCard(chatId, messageId, cardKey).then((ok) => {
|
|
6202
|
+
if (ok) this.updateCardStatus(chatId, statusText, cardKey);
|
|
6203
|
+
}).catch(() => {
|
|
6204
|
+
});
|
|
6205
|
+
return;
|
|
6206
|
+
}
|
|
6207
|
+
this.updateCardStatus(chatId, statusText, cardKey);
|
|
6208
|
+
}
|
|
6073
6209
|
async onStreamEnd(chatId, status, responseText, streamKey) {
|
|
6074
6210
|
if (!this.isStreamingEnabled()) return false;
|
|
6075
6211
|
return this.finalizeCard(chatId, status, responseText, streamKey);
|
|
@@ -16201,6 +16337,19 @@ function toUserVisibleCommandError(command, error) {
|
|
|
16201
16337
|
}
|
|
16202
16338
|
return `${command} \u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`;
|
|
16203
16339
|
}
|
|
16340
|
+
function pad2(value) {
|
|
16341
|
+
return String(value).padStart(2, "0");
|
|
16342
|
+
}
|
|
16343
|
+
function formatCommandDateTime(value) {
|
|
16344
|
+
const trimmed = value?.trim();
|
|
16345
|
+
if (!trimmed) return "-";
|
|
16346
|
+
const date = new Date(trimmed);
|
|
16347
|
+
if (Number.isNaN(date.getTime())) return trimmed;
|
|
16348
|
+
return [
|
|
16349
|
+
`${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`,
|
|
16350
|
+
`${pad2(date.getHours())}:${pad2(date.getMinutes())}:${pad2(date.getSeconds())}`
|
|
16351
|
+
].join(" ");
|
|
16352
|
+
}
|
|
16204
16353
|
function stripStoredAttachmentMarker(content) {
|
|
16205
16354
|
return content.replace(/\n?<!--files:[\s\S]*?-->$/u, "").trim();
|
|
16206
16355
|
}
|
|
@@ -16258,7 +16407,7 @@ function formatRuntimeStatus(session) {
|
|
|
16258
16407
|
}
|
|
16259
16408
|
function formatMirrorStatus(session) {
|
|
16260
16409
|
if (session?.mirror_status === "watching") {
|
|
16261
|
-
return session.mirror_last_event_at ? `\u76D1\u542C\u4E2D \xB7 \u6700\u8FD1\u540C\u6B65 ${session.mirror_last_event_at}` : "\u76D1\u542C\u4E2D";
|
|
16410
|
+
return session.mirror_last_event_at ? `\u76D1\u542C\u4E2D \xB7 \u6700\u8FD1\u540C\u6B65 ${formatCommandDateTime(session.mirror_last_event_at)}` : "\u76D1\u542C\u4E2D";
|
|
16262
16411
|
}
|
|
16263
16412
|
if (session?.mirror_status === "stale") {
|
|
16264
16413
|
return "\u5F85\u6062\u590D\uFF08\u6682\u65F6\u6CA1\u5B9A\u4F4D\u5230\u684C\u9762 thread \u6587\u4EF6\uFF09";
|
|
@@ -16294,14 +16443,15 @@ function formatCommandTimestamp(value) {
|
|
|
16294
16443
|
if (!trimmed) return "-";
|
|
16295
16444
|
const parsed = Date.parse(trimmed);
|
|
16296
16445
|
if (!Number.isFinite(parsed)) return trimmed;
|
|
16446
|
+
const localized = formatCommandDateTime(trimmed);
|
|
16297
16447
|
const diffMs = Math.max(0, Date.now() - parsed);
|
|
16298
16448
|
const diffMinutes = Math.floor(diffMs / 6e4);
|
|
16299
|
-
if (diffMinutes < 1) return `${
|
|
16300
|
-
if (diffMinutes < 60) return `${
|
|
16449
|
+
if (diffMinutes < 1) return `${localized}\uFF08\u521A\u521A\uFF09`;
|
|
16450
|
+
if (diffMinutes < 60) return `${localized}\uFF08${diffMinutes} \u5206\u949F\u524D\uFF09`;
|
|
16301
16451
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
16302
|
-
if (diffHours < 24) return `${
|
|
16452
|
+
if (diffHours < 24) return `${localized}\uFF08${diffHours} \u5C0F\u65F6\u524D\uFF09`;
|
|
16303
16453
|
const diffDays = Math.floor(diffHours / 24);
|
|
16304
|
-
return `${
|
|
16454
|
+
return `${localized}\uFF08${diffDays} \u5929\u524D\uFF09`;
|
|
16305
16455
|
}
|
|
16306
16456
|
function formatHealthProcessProbe(diagnosis) {
|
|
16307
16457
|
if (!diagnosis.processProbe) {
|
|
@@ -17891,7 +18041,7 @@ async function handleBridgeCommand(adapter, msg, text2, deps) {
|
|
|
17891
18041
|
[
|
|
17892
18042
|
["\u6807\u9898", getSessionDisplayName(draftSession, draftSession.working_directory)],
|
|
17893
18043
|
["\u76EE\u5F55", formatCommandPath(draftSession.working_directory)],
|
|
17894
|
-
["\u8FC7\u671F\u65F6\u95F4", draftSession.expires_at
|
|
18044
|
+
["\u8FC7\u671F\u65F6\u95F4", formatCommandDateTime(draftSession.expires_at)],
|
|
17895
18045
|
["\u6A21\u5F0F", "ask"]
|
|
17896
18046
|
],
|
|
17897
18047
|
["\u8FD9\u662F\u9690\u85CF\u7684\u8349\u7A3F\u7EBF\u7A0B\uFF0C\u4E0D\u4F1A\u51FA\u73B0\u5728\u5E38\u89C4\u4F1A\u8BDD\u5217\u8868\u4E2D\u3002"],
|
|
@@ -18775,6 +18925,16 @@ function pushStreamFeedbackTools(target, tools) {
|
|
|
18775
18925
|
} catch {
|
|
18776
18926
|
}
|
|
18777
18927
|
}
|
|
18928
|
+
function pushStreamFeedbackStatus(target, text2) {
|
|
18929
|
+
if (typeof target.adapter.onStreamStatus !== "function") return;
|
|
18930
|
+
target.ensureStarted?.();
|
|
18931
|
+
const rendered = renderFeedbackTextForChannel(target.channelType, text2);
|
|
18932
|
+
if (!rendered) return;
|
|
18933
|
+
try {
|
|
18934
|
+
target.adapter.onStreamStatus(target.chatId, rendered, target.streamKey);
|
|
18935
|
+
} catch {
|
|
18936
|
+
}
|
|
18937
|
+
}
|
|
18778
18938
|
async function finalizeStreamFeedback(target, status, text2) {
|
|
18779
18939
|
if (typeof target.adapter.onStreamEnd !== "function") return false;
|
|
18780
18940
|
const rendered = renderFeedbackTextForChannel(target.channelType, text2);
|
|
@@ -18793,6 +18953,7 @@ var STREAM_DEFAULTS = {
|
|
|
18793
18953
|
telegram: { intervalMs: 700, minDeltaChars: 20, maxChars: 3900 },
|
|
18794
18954
|
discord: { intervalMs: 1500, minDeltaChars: 40, maxChars: 1900 }
|
|
18795
18955
|
};
|
|
18956
|
+
var STREAM_STATUS_HEARTBEAT_MS = 1e4;
|
|
18796
18957
|
function getStreamConfig(channelType = "telegram") {
|
|
18797
18958
|
const { store } = getBridgeContext();
|
|
18798
18959
|
const defaults = STREAM_DEFAULTS[channelType] || STREAM_DEFAULTS.telegram;
|
|
@@ -18812,12 +18973,40 @@ function flushPreview(adapter, state, config2) {
|
|
|
18812
18973
|
}).catch(() => {
|
|
18813
18974
|
});
|
|
18814
18975
|
}
|
|
18976
|
+
function formatRuntimeDuration(ms) {
|
|
18977
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
18978
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
18979
|
+
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
18980
|
+
const seconds = totalSeconds % 60;
|
|
18981
|
+
if (totalMinutes < 60) {
|
|
18982
|
+
return seconds > 0 ? `${totalMinutes}m ${seconds}s` : `${totalMinutes}m`;
|
|
18983
|
+
}
|
|
18984
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
18985
|
+
const minutes = totalMinutes % 60;
|
|
18986
|
+
if (minutes === 0 && seconds === 0) return `${hours}h`;
|
|
18987
|
+
if (seconds === 0) return `${hours}h ${minutes}m`;
|
|
18988
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
|
18989
|
+
}
|
|
18990
|
+
function formatInteractiveRuntimeStatus(elapsedMs, silentMs) {
|
|
18991
|
+
const parts = [`\u5DF2\u8FD0\u884C ${formatRuntimeDuration(elapsedMs)}`];
|
|
18992
|
+
if (typeof silentMs === "number" && silentMs >= 0) {
|
|
18993
|
+
parts.push(`\u6700\u8FD1 ${formatRuntimeDuration(silentMs)} \u65E0\u65B0\u8F93\u51FA`);
|
|
18994
|
+
}
|
|
18995
|
+
return parts.join("\uFF0C");
|
|
18996
|
+
}
|
|
18815
18997
|
async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
18816
18998
|
const binding = resolve(msg.address);
|
|
18817
18999
|
const streamKey = buildInteractiveStreamKey(binding.codepilotSessionId, msg.messageId);
|
|
19000
|
+
const nowMs = deps.nowMs ?? (() => Date.now());
|
|
19001
|
+
const setIntervalFn = deps.setIntervalFn ?? ((callback, intervalMs) => setInterval(callback, intervalMs));
|
|
19002
|
+
const clearIntervalFn = deps.clearIntervalFn ?? ((handle) => clearInterval(handle));
|
|
19003
|
+
const processMessageImpl = deps.processMessageImpl ?? processMessage;
|
|
19004
|
+
const forwardPermissionRequestImpl = deps.forwardPermissionRequestImpl ?? forwardPermissionRequest;
|
|
19005
|
+
const streamStatusHeartbeatMs = Math.max(1e3, deps.streamStatusHeartbeatMs ?? STREAM_STATUS_HEARTBEAT_MS);
|
|
18818
19006
|
adapter.onMessageStart?.(msg.address.chatId, streamKey);
|
|
18819
19007
|
const taskAbort = new AbortController();
|
|
18820
19008
|
const taskId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
19009
|
+
const taskStartedAt = nowMs();
|
|
18821
19010
|
deps.resetMirrorSessionForInteractiveRun(binding.codepilotSessionId);
|
|
18822
19011
|
const taskState = {
|
|
18823
19012
|
id: taskId,
|
|
@@ -18828,7 +19017,8 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
18828
19017
|
streamKey,
|
|
18829
19018
|
sessionId: binding.codepilotSessionId,
|
|
18830
19019
|
hasStreamingCards: false,
|
|
18831
|
-
|
|
19020
|
+
structuredStreamUiActive: false,
|
|
19021
|
+
lastActivityAt: taskStartedAt,
|
|
18832
19022
|
idleReminderSent: false,
|
|
18833
19023
|
streamFinalized: false,
|
|
18834
19024
|
uiEnded: false,
|
|
@@ -18891,6 +19081,33 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
18891
19081
|
chatId: msg.address.chatId,
|
|
18892
19082
|
streamKey
|
|
18893
19083
|
};
|
|
19084
|
+
const supportsPersistentStreamStatus = hasStreamingCards && adapter.provider === "feishu" && typeof adapter.onStreamStatus === "function";
|
|
19085
|
+
const supportsStructuredStreamUi = supportsPersistentStreamStatus && (adapter.supportsStructuredStreamingUi?.(msg.address.chatId) ?? true);
|
|
19086
|
+
const syncStructuredStreamUiState = () => {
|
|
19087
|
+
if (!supportsStructuredStreamUi || taskState.structuredStreamUiActive) return;
|
|
19088
|
+
if (adapter.hasActiveStreamingUi?.(msg.address.chatId, streamKey)) {
|
|
19089
|
+
taskState.structuredStreamUiActive = true;
|
|
19090
|
+
}
|
|
19091
|
+
};
|
|
19092
|
+
const pushRunningStatus = (silentMs) => {
|
|
19093
|
+
if (!supportsStructuredStreamUi || streamStatusUpdatesClosed) return;
|
|
19094
|
+
pushStreamFeedbackStatus(
|
|
19095
|
+
streamFeedbackTarget,
|
|
19096
|
+
formatInteractiveRuntimeStatus(nowMs() - taskStartedAt, silentMs)
|
|
19097
|
+
);
|
|
19098
|
+
syncStructuredStreamUiState();
|
|
19099
|
+
};
|
|
19100
|
+
let streamStatusHeartbeat = null;
|
|
19101
|
+
let streamStatusUpdatesClosed = false;
|
|
19102
|
+
const clearStreamStatusHeartbeat = () => {
|
|
19103
|
+
if (streamStatusHeartbeat == null) return;
|
|
19104
|
+
clearIntervalFn(streamStatusHeartbeat);
|
|
19105
|
+
streamStatusHeartbeat = null;
|
|
19106
|
+
};
|
|
19107
|
+
const stopStructuredStreamStatusUpdates = () => {
|
|
19108
|
+
streamStatusUpdatesClosed = true;
|
|
19109
|
+
clearStreamStatusHeartbeat();
|
|
19110
|
+
};
|
|
18894
19111
|
const onStreamCardText = hasStreamingCards ? (fullText) => {
|
|
18895
19112
|
if (!deps.isCurrentInteractiveTask(binding.codepilotSessionId, taskId)) return;
|
|
18896
19113
|
pushStreamFeedbackText(
|
|
@@ -18911,6 +19128,8 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
18911
19128
|
if (hasStreamingCards) {
|
|
18912
19129
|
pushStreamFeedbackTools(streamFeedbackTarget, Array.from(toolCallTracker.values()));
|
|
18913
19130
|
}
|
|
19131
|
+
pushRunningStatus(null);
|
|
19132
|
+
syncStructuredStreamUiState();
|
|
18914
19133
|
};
|
|
18915
19134
|
const onPartialText = (fullText) => {
|
|
18916
19135
|
if (!deps.isCurrentInteractiveTask(binding.codepilotSessionId, taskId)) return;
|
|
@@ -18918,17 +19137,36 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
18918
19137
|
deps.recordInteractiveHealthProgress(binding.codepilotSessionId, "text");
|
|
18919
19138
|
previewOnPartialText?.(fullText);
|
|
18920
19139
|
onStreamCardText?.(fullText);
|
|
19140
|
+
pushRunningStatus(null);
|
|
19141
|
+
syncStructuredStreamUiState();
|
|
18921
19142
|
};
|
|
19143
|
+
if (supportsStructuredStreamUi) {
|
|
19144
|
+
pushRunningStatus(null);
|
|
19145
|
+
streamStatusHeartbeat = setIntervalFn(() => {
|
|
19146
|
+
if (streamStatusUpdatesClosed) {
|
|
19147
|
+
clearStreamStatusHeartbeat();
|
|
19148
|
+
return;
|
|
19149
|
+
}
|
|
19150
|
+
if (!deps.isCurrentInteractiveTask(binding.codepilotSessionId, taskId) || taskAbort.signal.aborted) {
|
|
19151
|
+
clearStreamStatusHeartbeat();
|
|
19152
|
+
return;
|
|
19153
|
+
}
|
|
19154
|
+
const silentMs = nowMs() - taskState.lastActivityAt;
|
|
19155
|
+
if (silentMs < streamStatusHeartbeatMs) return;
|
|
19156
|
+
pushRunningStatus(silentMs);
|
|
19157
|
+
syncStructuredStreamUiState();
|
|
19158
|
+
}, streamStatusHeartbeatMs);
|
|
19159
|
+
}
|
|
18922
19160
|
let finalOutcome = "failed";
|
|
18923
19161
|
let finalOutcomeDetail;
|
|
18924
19162
|
let shouldRecordHealthEnd = true;
|
|
18925
19163
|
try {
|
|
18926
19164
|
const promptText = text2 || (attachments && attachments.length > 0 ? "Describe this image." : "");
|
|
18927
|
-
const result = await
|
|
19165
|
+
const result = await processMessageImpl(
|
|
18928
19166
|
binding,
|
|
18929
19167
|
promptText,
|
|
18930
19168
|
async (perm) => {
|
|
18931
|
-
await
|
|
19169
|
+
await forwardPermissionRequestImpl(
|
|
18932
19170
|
adapter,
|
|
18933
19171
|
msg.address,
|
|
18934
19172
|
perm.permissionRequestId,
|
|
@@ -18960,6 +19198,7 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
18960
19198
|
}
|
|
18961
19199
|
let cardFinalized = false;
|
|
18962
19200
|
if (hasStreamingCards) {
|
|
19201
|
+
stopStructuredStreamStatusUpdates();
|
|
18963
19202
|
cardFinalized = await finalizeStreamFeedback(
|
|
18964
19203
|
streamFeedbackTarget,
|
|
18965
19204
|
result.hasError ? "error" : "completed",
|
|
@@ -18994,6 +19233,7 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
18994
19233
|
finalOutcome = result.hasError ? "failed" : "completed";
|
|
18995
19234
|
finalOutcomeDetail = result.hasError ? result.errorMessage?.trim() || void 0 : void 0;
|
|
18996
19235
|
} finally {
|
|
19236
|
+
stopStructuredStreamStatusUpdates();
|
|
18997
19237
|
if (previewState) {
|
|
18998
19238
|
if (previewState.throttleTimer) {
|
|
18999
19239
|
clearTimeout(previewState.throttleTimer);
|
|
@@ -19038,6 +19278,9 @@ function buildInteractiveIdleReminderNotice() {
|
|
|
19038
19278
|
"\u7CFB\u7EDF\u4E0D\u4F1A\u81EA\u52A8\u7EC8\u6B62\u5B83\uFF1B\u5982\u679C\u4F60\u4ECD\u5728\u5BF9\u5E94\u7EBF\u7A0B\uFF0C\u53EF\u53D1\u9001 `/stop` \u4E3B\u52A8\u505C\u6B62\uFF1B\u5982\u679C\u5DF2\u7ECF\u5207\u5230\u522B\u7684\u7EBF\u7A0B\uFF0C\u9700\u8981\u5148\u5207\u56DE\u5BF9\u5E94\u7EBF\u7A0B\u3002"
|
|
19039
19279
|
].join("\n");
|
|
19040
19280
|
}
|
|
19281
|
+
function shouldSkipIdleReminder(task) {
|
|
19282
|
+
return task.adapter.provider === "feishu" && task.structuredStreamUiActive;
|
|
19283
|
+
}
|
|
19041
19284
|
function createInteractiveRuntime(getState2, options, deps) {
|
|
19042
19285
|
function getQueuedCount(sessionId) {
|
|
19043
19286
|
return getState2().queuedCounts.get(sessionId) || 0;
|
|
@@ -19096,6 +19339,7 @@ function createInteractiveRuntime(getState2, options, deps) {
|
|
|
19096
19339
|
const now2 = Date.now();
|
|
19097
19340
|
const tasks = Array.from(getState2().activeTasks.values());
|
|
19098
19341
|
for (const task of tasks) {
|
|
19342
|
+
if (shouldSkipIdleReminder(task)) continue;
|
|
19099
19343
|
if (task.idleReminderSent) continue;
|
|
19100
19344
|
if (now2 - task.lastActivityAt < options.idleReminderMs) continue;
|
|
19101
19345
|
await remindIdleInteractiveTask(task);
|
package/package.json
CHANGED