codex-to-im 1.0.40 → 1.0.41
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 +920 -139
- package/package.json +2 -2
- package/scripts/patch-codex-sdk-windows-hide.js +1 -1
package/dist/daemon.mjs
CHANGED
|
@@ -3959,6 +3959,55 @@ function shouldRetryFreshThread(message) {
|
|
|
3959
3959
|
const lower = message.toLowerCase();
|
|
3960
3960
|
return lower.includes("resuming session with different model") || lower.includes("no such session") || lower.includes("resume") && lower.includes("session");
|
|
3961
3961
|
}
|
|
3962
|
+
function normalizeCodexErrorMessage(message) {
|
|
3963
|
+
const trimmed = (message || "").trim();
|
|
3964
|
+
if (!trimmed) return "Codex \u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
3965
|
+
const lower = trimmed.toLowerCase();
|
|
3966
|
+
if (lower.includes("timeout waiting for child process to exit") || lower.includes("reconnecting...")) {
|
|
3967
|
+
return "Codex \u4F1A\u8BDD\u6062\u590D\u5931\u8D25\uFF0C\u4E0A\u4E00\u8F6E\u6267\u884C\u8FDB\u7A0B\u672A\u6B63\u5E38\u9000\u51FA\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u5982\u679C\u8FDE\u7EED\u5931\u8D25\uFF0C\u8BF7\u65B0\u5F00\u7EBF\u7A0B\u6216\u5207\u6362\u5230 `/t 0`\u3002";
|
|
3968
|
+
}
|
|
3969
|
+
return trimmed;
|
|
3970
|
+
}
|
|
3971
|
+
function normalizeTaskText(value) {
|
|
3972
|
+
return typeof value === "string" ? value.trim() : "";
|
|
3973
|
+
}
|
|
3974
|
+
function mapTodoListItems(items) {
|
|
3975
|
+
if (!Array.isArray(items)) return [];
|
|
3976
|
+
const normalized = items.map((item) => ({
|
|
3977
|
+
text: normalizeTaskText(item?.text),
|
|
3978
|
+
completed: item?.completed === true
|
|
3979
|
+
})).filter((item) => item.text);
|
|
3980
|
+
let firstIncompleteSeen = false;
|
|
3981
|
+
return normalized.map((item) => {
|
|
3982
|
+
if (item.completed) {
|
|
3983
|
+
return { text: item.text, status: "completed" };
|
|
3984
|
+
}
|
|
3985
|
+
if (!firstIncompleteSeen) {
|
|
3986
|
+
firstIncompleteSeen = true;
|
|
3987
|
+
return { text: item.text, status: "in_progress" };
|
|
3988
|
+
}
|
|
3989
|
+
return { text: item.text, status: "pending" };
|
|
3990
|
+
});
|
|
3991
|
+
}
|
|
3992
|
+
function extractMcpContentText(value) {
|
|
3993
|
+
if (!Array.isArray(value)) return "";
|
|
3994
|
+
return value.map((block2) => {
|
|
3995
|
+
if (!block2 || typeof block2 !== "object") return "";
|
|
3996
|
+
const record = block2;
|
|
3997
|
+
if (typeof record.text === "string") return record.text.trim();
|
|
3998
|
+
if (typeof record.content === "string") return record.content.trim();
|
|
3999
|
+
return "";
|
|
4000
|
+
}).filter(Boolean).join("\n\n").trim();
|
|
4001
|
+
}
|
|
4002
|
+
function stringifyUnknown(value) {
|
|
4003
|
+
if (typeof value === "string") return value;
|
|
4004
|
+
if (value == null) return "";
|
|
4005
|
+
try {
|
|
4006
|
+
return JSON.stringify(value);
|
|
4007
|
+
} catch {
|
|
4008
|
+
return String(value);
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
3962
4011
|
var MIME_EXT, CodexProvider;
|
|
3963
4012
|
var init_codex_provider = __esm({
|
|
3964
4013
|
"src/codex-provider.ts"() {
|
|
@@ -4047,6 +4096,7 @@ var init_codex_provider = __esm({
|
|
|
4047
4096
|
input = params.prompt;
|
|
4048
4097
|
}
|
|
4049
4098
|
let retryFresh = false;
|
|
4099
|
+
const emittedToolStarts = /* @__PURE__ */ new Set();
|
|
4050
4100
|
while (true) {
|
|
4051
4101
|
let thread;
|
|
4052
4102
|
if (savedThreadId) {
|
|
@@ -4078,9 +4128,19 @@ var init_codex_provider = __esm({
|
|
|
4078
4128
|
}));
|
|
4079
4129
|
break;
|
|
4080
4130
|
}
|
|
4131
|
+
case "turn.started":
|
|
4132
|
+
break;
|
|
4133
|
+
case "item.started":
|
|
4134
|
+
case "item.updated":
|
|
4081
4135
|
case "item.completed": {
|
|
4082
4136
|
const item = event.item;
|
|
4083
|
-
self.
|
|
4137
|
+
self.handleItemEvent(
|
|
4138
|
+
controller,
|
|
4139
|
+
item,
|
|
4140
|
+
event.type === "item.started" ? "started" : event.type === "item.updated" ? "updated" : "completed",
|
|
4141
|
+
params.sessionId,
|
|
4142
|
+
emittedToolStarts
|
|
4143
|
+
);
|
|
4084
4144
|
break;
|
|
4085
4145
|
}
|
|
4086
4146
|
case "turn.completed": {
|
|
@@ -4098,17 +4158,25 @@ var init_codex_provider = __esm({
|
|
|
4098
4158
|
break;
|
|
4099
4159
|
}
|
|
4100
4160
|
case "turn.failed": {
|
|
4101
|
-
const error = event.message;
|
|
4102
|
-
controller.enqueue(sseEvent("error", error || "Turn failed"));
|
|
4161
|
+
const error = event.error?.message;
|
|
4162
|
+
controller.enqueue(sseEvent("error", normalizeCodexErrorMessage(error || "Turn failed")));
|
|
4103
4163
|
sawTerminalEvent = true;
|
|
4104
4164
|
break;
|
|
4105
4165
|
}
|
|
4106
4166
|
case "error": {
|
|
4107
4167
|
const error = event.message;
|
|
4108
|
-
controller.enqueue(sseEvent("error", error || "Thread error"));
|
|
4168
|
+
controller.enqueue(sseEvent("error", normalizeCodexErrorMessage(error || "Thread error")));
|
|
4109
4169
|
sawTerminalEvent = true;
|
|
4110
4170
|
break;
|
|
4111
4171
|
}
|
|
4172
|
+
default: {
|
|
4173
|
+
const exhaustiveEvent = event;
|
|
4174
|
+
console.warn(
|
|
4175
|
+
"[codex-provider] Unhandled thread event:",
|
|
4176
|
+
stringifyUnknown(exhaustiveEvent)
|
|
4177
|
+
);
|
|
4178
|
+
break;
|
|
4179
|
+
}
|
|
4112
4180
|
}
|
|
4113
4181
|
if (sawTerminalEvent) {
|
|
4114
4182
|
break;
|
|
@@ -4131,7 +4199,7 @@ var init_codex_provider = __esm({
|
|
|
4131
4199
|
const message = err instanceof Error ? err.message : String(err);
|
|
4132
4200
|
console.error("[codex-provider] Error:", err instanceof Error ? err.stack || err.message : err);
|
|
4133
4201
|
try {
|
|
4134
|
-
controller.enqueue(sseEvent("error", message));
|
|
4202
|
+
controller.enqueue(sseEvent("error", normalizeCodexErrorMessage(message)));
|
|
4135
4203
|
controller.close();
|
|
4136
4204
|
} catch {
|
|
4137
4205
|
}
|
|
@@ -4148,12 +4216,22 @@ var init_codex_provider = __esm({
|
|
|
4148
4216
|
});
|
|
4149
4217
|
}
|
|
4150
4218
|
/**
|
|
4151
|
-
* Map a
|
|
4219
|
+
* Map a Codex item event to SSE events.
|
|
4152
4220
|
*/
|
|
4153
|
-
|
|
4221
|
+
handleItemEvent(controller, item, phase, sessionId, emittedToolStarts) {
|
|
4154
4222
|
const itemType = item.type;
|
|
4223
|
+
const ensureToolUse = (toolId, name, input) => {
|
|
4224
|
+
if (emittedToolStarts.has(toolId)) return;
|
|
4225
|
+
emittedToolStarts.add(toolId);
|
|
4226
|
+
controller.enqueue(sseEvent("tool_use", {
|
|
4227
|
+
id: toolId,
|
|
4228
|
+
name,
|
|
4229
|
+
input
|
|
4230
|
+
}));
|
|
4231
|
+
};
|
|
4155
4232
|
switch (itemType) {
|
|
4156
4233
|
case "agent_message": {
|
|
4234
|
+
if (phase !== "completed") break;
|
|
4157
4235
|
const text2 = item.text || "";
|
|
4158
4236
|
if (text2) {
|
|
4159
4237
|
controller.enqueue(sseEvent("text", text2));
|
|
@@ -4165,12 +4243,11 @@ var init_codex_provider = __esm({
|
|
|
4165
4243
|
const command = item.command || "";
|
|
4166
4244
|
const output = item.aggregated_output || "";
|
|
4167
4245
|
const exitCode = item.exit_code;
|
|
4246
|
+
const status = item.status;
|
|
4168
4247
|
const isError = exitCode != null && exitCode !== 0;
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
input: { command }
|
|
4173
|
-
}));
|
|
4248
|
+
const terminal = phase === "completed" || status === "completed" || status === "failed";
|
|
4249
|
+
ensureToolUse(toolId, "Bash", { command });
|
|
4250
|
+
if (!terminal) break;
|
|
4174
4251
|
const resultContent = output || (isError ? `Exit code: ${exitCode}` : "Done");
|
|
4175
4252
|
controller.enqueue(sseEvent("tool_result", {
|
|
4176
4253
|
tool_use_id: toolId,
|
|
@@ -4180,14 +4257,11 @@ var init_codex_provider = __esm({
|
|
|
4180
4257
|
break;
|
|
4181
4258
|
}
|
|
4182
4259
|
case "file_change": {
|
|
4260
|
+
if (phase !== "completed") break;
|
|
4183
4261
|
const toolId = item.id || `tool-${Date.now()}`;
|
|
4184
4262
|
const changes = item.changes || [];
|
|
4185
4263
|
const summary = changes.map((c) => `${c.kind}: ${c.path}`).join("\n");
|
|
4186
|
-
|
|
4187
|
-
id: toolId,
|
|
4188
|
-
name: "Edit",
|
|
4189
|
-
input: { files: changes }
|
|
4190
|
-
}));
|
|
4264
|
+
ensureToolUse(toolId, "Edit", { files: changes });
|
|
4191
4265
|
controller.enqueue(sseEvent("tool_result", {
|
|
4192
4266
|
tool_use_id: toolId,
|
|
4193
4267
|
content: summary || "File changes applied",
|
|
@@ -4202,13 +4276,11 @@ var init_codex_provider = __esm({
|
|
|
4202
4276
|
const args = item.arguments;
|
|
4203
4277
|
const result = item.result;
|
|
4204
4278
|
const error = item.error;
|
|
4205
|
-
const
|
|
4206
|
-
const
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
input: args
|
|
4211
|
-
}));
|
|
4279
|
+
const status = item.status;
|
|
4280
|
+
const terminal = phase === "completed" || status === "completed" || status === "failed";
|
|
4281
|
+
const resultText = extractMcpContentText(result?.content) || stringifyUnknown(result?.structured_content) || stringifyUnknown(result?.content);
|
|
4282
|
+
ensureToolUse(toolId, `mcp__${server}__${tool}`, args);
|
|
4283
|
+
if (!terminal) break;
|
|
4212
4284
|
controller.enqueue(sseEvent("tool_result", {
|
|
4213
4285
|
tool_use_id: toolId,
|
|
4214
4286
|
content: error?.message || resultText || "Done",
|
|
@@ -4216,6 +4288,18 @@ var init_codex_provider = __esm({
|
|
|
4216
4288
|
}));
|
|
4217
4289
|
break;
|
|
4218
4290
|
}
|
|
4291
|
+
case "web_search": {
|
|
4292
|
+
const toolId = item.id || `tool-${Date.now()}`;
|
|
4293
|
+
const query2 = item.query || "";
|
|
4294
|
+
ensureToolUse(toolId, "Web Search", { query: query2 });
|
|
4295
|
+
if (phase !== "completed") break;
|
|
4296
|
+
controller.enqueue(sseEvent("tool_result", {
|
|
4297
|
+
tool_use_id: toolId,
|
|
4298
|
+
content: query2 || "Search completed",
|
|
4299
|
+
is_error: false
|
|
4300
|
+
}));
|
|
4301
|
+
break;
|
|
4302
|
+
}
|
|
4219
4303
|
case "reasoning": {
|
|
4220
4304
|
const text2 = item.text || "";
|
|
4221
4305
|
if (text2) {
|
|
@@ -4223,8 +4307,33 @@ var init_codex_provider = __esm({
|
|
|
4223
4307
|
}
|
|
4224
4308
|
break;
|
|
4225
4309
|
}
|
|
4310
|
+
case "todo_list": {
|
|
4311
|
+
const tasks = mapTodoListItems(item.items);
|
|
4312
|
+
controller.enqueue(sseEvent("task_update", {
|
|
4313
|
+
session_id: sessionId,
|
|
4314
|
+
sdk_session_id: this.threadIds.get(sessionId) || void 0,
|
|
4315
|
+
tasks,
|
|
4316
|
+
todos: tasks
|
|
4317
|
+
}));
|
|
4318
|
+
break;
|
|
4319
|
+
}
|
|
4320
|
+
case "error": {
|
|
4321
|
+
controller.enqueue(sseEvent("error", normalizeCodexErrorMessage(item.message || "Codex error")));
|
|
4322
|
+
break;
|
|
4323
|
+
}
|
|
4324
|
+
default: {
|
|
4325
|
+
const exhaustiveItem = item;
|
|
4326
|
+
console.warn(
|
|
4327
|
+
"[codex-provider] Unhandled thread item:",
|
|
4328
|
+
stringifyUnknown(exhaustiveItem)
|
|
4329
|
+
);
|
|
4330
|
+
break;
|
|
4331
|
+
}
|
|
4226
4332
|
}
|
|
4227
4333
|
}
|
|
4334
|
+
handleCompletedItem(controller, item) {
|
|
4335
|
+
this.handleItemEvent(controller, item, "completed", "test-session", /* @__PURE__ */ new Set());
|
|
4336
|
+
}
|
|
4228
4337
|
};
|
|
4229
4338
|
}
|
|
4230
4339
|
});
|
|
@@ -5515,6 +5624,14 @@ function buildToolProgressMarkdown(tools) {
|
|
|
5515
5624
|
});
|
|
5516
5625
|
return lines.join("\n");
|
|
5517
5626
|
}
|
|
5627
|
+
function buildTaskProgressMarkdown(tasks) {
|
|
5628
|
+
if (tasks.length === 0) return "";
|
|
5629
|
+
return tasks.map((task) => {
|
|
5630
|
+
const icon = task.status === "completed" ? "\u2705" : task.status === "in_progress" ? "\u{1F504}" : "\u23F3";
|
|
5631
|
+
const label = task.status === "completed" ? "\u5DF2\u5B8C\u6210" : task.status === "in_progress" ? "\u6267\u884C\u4E2D" : "\u7B49\u5F85\u4E2D";
|
|
5632
|
+
return `${icon} ${task.text}\uFF08${label}\uFF09`;
|
|
5633
|
+
}).join("\n");
|
|
5634
|
+
}
|
|
5518
5635
|
function formatElapsed(ms) {
|
|
5519
5636
|
if (ms < 1e3) return `${ms}ms`;
|
|
5520
5637
|
const sec = ms / 1e3;
|
|
@@ -5529,9 +5646,13 @@ function buildStreamingTextContent(text2) {
|
|
|
5529
5646
|
function buildStreamingToolsContent(tools) {
|
|
5530
5647
|
return buildToolProgressMarkdown(tools);
|
|
5531
5648
|
}
|
|
5532
|
-
function
|
|
5649
|
+
function buildStreamingTaskContent(tasks) {
|
|
5650
|
+
return buildTaskProgressMarkdown(tasks);
|
|
5651
|
+
}
|
|
5652
|
+
function buildFinalCardJson(text2, tasks, tools, footer) {
|
|
5533
5653
|
const elements = [];
|
|
5534
5654
|
const content = preprocessFeishuMarkdown(text2);
|
|
5655
|
+
const taskMd = buildTaskProgressMarkdown(tasks);
|
|
5535
5656
|
const toolMd = buildToolProgressMarkdown(tools);
|
|
5536
5657
|
if (content) {
|
|
5537
5658
|
elements.push({
|
|
@@ -5541,6 +5662,17 @@ function buildFinalCardJson(text2, tools, footer) {
|
|
|
5541
5662
|
text_size: "normal"
|
|
5542
5663
|
});
|
|
5543
5664
|
}
|
|
5665
|
+
if (taskMd) {
|
|
5666
|
+
if (elements.length > 0) {
|
|
5667
|
+
elements.push({ tag: "hr" });
|
|
5668
|
+
}
|
|
5669
|
+
elements.push({
|
|
5670
|
+
tag: "markdown",
|
|
5671
|
+
content: taskMd,
|
|
5672
|
+
text_align: "left",
|
|
5673
|
+
text_size: "normal"
|
|
5674
|
+
});
|
|
5675
|
+
}
|
|
5544
5676
|
if (toolMd) {
|
|
5545
5677
|
if (elements.length > 0) {
|
|
5546
5678
|
elements.push({ tag: "hr" });
|
|
@@ -5626,7 +5758,9 @@ var DEDUP_MAX = 1e3;
|
|
|
5626
5758
|
var MAX_FILE_SIZE = 20 * 1024 * 1024;
|
|
5627
5759
|
var TYPING_EMOJI = "Typing";
|
|
5628
5760
|
var CARD_THROTTLE_MS = 200;
|
|
5761
|
+
var CARD_REQUEST_TIMEOUT_MS = 15e3;
|
|
5629
5762
|
var INITIAL_STREAMING_STATUS = "\u5904\u7406\u4E2D";
|
|
5763
|
+
var EMPTY_STREAMING_TASKS = "";
|
|
5630
5764
|
var EMPTY_STREAMING_TOOLS = "";
|
|
5631
5765
|
var MIME_BY_TYPE = {
|
|
5632
5766
|
image: "image/png",
|
|
@@ -5657,6 +5791,7 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5657
5791
|
cardCreatePromises = /* @__PURE__ */ new Map();
|
|
5658
5792
|
/** Cached tenant token for upload APIs. */
|
|
5659
5793
|
tenantTokenCache = null;
|
|
5794
|
+
cardRequestTimeoutMs = CARD_REQUEST_TIMEOUT_MS;
|
|
5660
5795
|
constructor(instance) {
|
|
5661
5796
|
super();
|
|
5662
5797
|
this.channelType = instance?.id || "feishu";
|
|
@@ -5884,6 +6019,13 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5884
6019
|
text_size: "normal",
|
|
5885
6020
|
element_id: "streaming_content"
|
|
5886
6021
|
},
|
|
6022
|
+
{
|
|
6023
|
+
tag: "markdown",
|
|
6024
|
+
content: EMPTY_STREAMING_TASKS,
|
|
6025
|
+
text_align: "left",
|
|
6026
|
+
text_size: "normal",
|
|
6027
|
+
element_id: "streaming_tasks"
|
|
6028
|
+
},
|
|
5887
6029
|
{
|
|
5888
6030
|
tag: "markdown",
|
|
5889
6031
|
content: EMPTY_STREAMING_TOOLS,
|
|
@@ -5901,9 +6043,9 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5901
6043
|
]
|
|
5902
6044
|
}
|
|
5903
6045
|
};
|
|
5904
|
-
const createResp = await cardkit.card.create({
|
|
6046
|
+
const createResp = await this.withFeishuRequestTimeout(cardKey, "card.create", () => cardkit.card.create({
|
|
5905
6047
|
data: { type: "card_json", data: JSON.stringify(cardBody) }
|
|
5906
|
-
});
|
|
6048
|
+
}));
|
|
5907
6049
|
const cardId = createResp?.data?.card_id;
|
|
5908
6050
|
if (!cardId) {
|
|
5909
6051
|
console.warn("[feishu-adapter] Card create returned no card_id");
|
|
@@ -5912,19 +6054,19 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5912
6054
|
const cardContent = JSON.stringify({ type: "card", data: { card_id: cardId } });
|
|
5913
6055
|
let msgResp;
|
|
5914
6056
|
if (replyToMessageId) {
|
|
5915
|
-
msgResp = await this.restClient.im.message.reply({
|
|
6057
|
+
msgResp = await this.withFeishuRequestTimeout(cardKey, "im.message.reply:interactive", () => this.restClient.im.message.reply({
|
|
5916
6058
|
path: { message_id: replyToMessageId },
|
|
5917
6059
|
data: { content: cardContent, msg_type: "interactive" }
|
|
5918
|
-
});
|
|
6060
|
+
}));
|
|
5919
6061
|
} else {
|
|
5920
|
-
msgResp = await this.restClient.im.message.create({
|
|
6062
|
+
msgResp = await this.withFeishuRequestTimeout(cardKey, "im.message.create:interactive", () => this.restClient.im.message.create({
|
|
5921
6063
|
params: { receive_id_type: "chat_id" },
|
|
5922
6064
|
data: {
|
|
5923
6065
|
receive_id: chatId,
|
|
5924
6066
|
msg_type: "interactive",
|
|
5925
6067
|
content: cardContent
|
|
5926
6068
|
}
|
|
5927
|
-
});
|
|
6069
|
+
}));
|
|
5928
6070
|
}
|
|
5929
6071
|
const messageId = msgResp?.data?.message_id;
|
|
5930
6072
|
if (!messageId) {
|
|
@@ -5937,17 +6079,25 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5937
6079
|
messageId,
|
|
5938
6080
|
sequence: 0,
|
|
5939
6081
|
startTime: Date.now(),
|
|
6082
|
+
taskItems: [],
|
|
5940
6083
|
toolCalls: [],
|
|
5941
6084
|
thinking: true,
|
|
5942
6085
|
pendingText: null,
|
|
6086
|
+
pendingTasksText: EMPTY_STREAMING_TASKS,
|
|
5943
6087
|
pendingStatusText: INITIAL_STREAMING_STATUS,
|
|
5944
6088
|
renderedText: "\u{1F4AD} Thinking...",
|
|
6089
|
+
renderedTasksText: EMPTY_STREAMING_TASKS,
|
|
5945
6090
|
renderedToolsText: EMPTY_STREAMING_TOOLS,
|
|
5946
6091
|
renderedStatusText: INITIAL_STREAMING_STATUS,
|
|
5947
6092
|
lastUpdateAt: 0,
|
|
5948
6093
|
throttleTimer: null,
|
|
5949
6094
|
flushInFlight: null,
|
|
5950
|
-
flushQueued: false
|
|
6095
|
+
flushQueued: false,
|
|
6096
|
+
lastFlushStartedAt: null,
|
|
6097
|
+
lastSuccessfulFlushAt: null,
|
|
6098
|
+
lastFlushErrorAt: null,
|
|
6099
|
+
lastFlushError: null,
|
|
6100
|
+
consecutiveFlushFailures: 0
|
|
5951
6101
|
});
|
|
5952
6102
|
console.log(`[feishu-adapter] Streaming card created: streamKey=${cardKey}, cardId=${cardId}, msgId=${messageId}`);
|
|
5953
6103
|
return true;
|
|
@@ -5976,6 +6126,14 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5976
6126
|
state.pendingStatusText = statusText || INITIAL_STREAMING_STATUS;
|
|
5977
6127
|
this.scheduleCardFlush(cardKey);
|
|
5978
6128
|
}
|
|
6129
|
+
updateTaskProgress(chatId, tasks, streamKey) {
|
|
6130
|
+
const cardKey = this.resolveStreamKey(chatId, streamKey);
|
|
6131
|
+
const state = this.activeCards.get(cardKey);
|
|
6132
|
+
if (!state) return;
|
|
6133
|
+
state.taskItems = tasks;
|
|
6134
|
+
state.pendingTasksText = buildStreamingTaskContent(tasks) || EMPTY_STREAMING_TASKS;
|
|
6135
|
+
this.scheduleCardFlush(cardKey);
|
|
6136
|
+
}
|
|
5979
6137
|
enqueueCardFlush(streamKey) {
|
|
5980
6138
|
const state = this.activeCards.get(streamKey);
|
|
5981
6139
|
if (!state) return;
|
|
@@ -5983,6 +6141,7 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
5983
6141
|
state.flushQueued = true;
|
|
5984
6142
|
return;
|
|
5985
6143
|
}
|
|
6144
|
+
state.lastFlushStartedAt = Date.now();
|
|
5986
6145
|
state.flushInFlight = this.flushCardUpdate(streamKey).catch((err) => {
|
|
5987
6146
|
console.warn("[feishu-adapter] cardElement.content failed:", err instanceof Error ? err.message : err);
|
|
5988
6147
|
}).finally(() => {
|
|
@@ -6023,6 +6182,7 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
6023
6182
|
const cardkit = this.restClient.cardkit?.v1;
|
|
6024
6183
|
if (!cardkit?.cardElement?.content) return;
|
|
6025
6184
|
const content = buildStreamingTextContent(state.pendingText || "");
|
|
6185
|
+
const tasksText = state.pendingTasksText || EMPTY_STREAMING_TASKS;
|
|
6026
6186
|
const toolsText = buildStreamingToolsContent(state.toolCalls) || EMPTY_STREAMING_TOOLS;
|
|
6027
6187
|
const statusText = state.pendingStatusText || INITIAL_STREAMING_STATUS;
|
|
6028
6188
|
const updates = [];
|
|
@@ -6035,6 +6195,15 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
6035
6195
|
}
|
|
6036
6196
|
});
|
|
6037
6197
|
}
|
|
6198
|
+
if (tasksText !== state.renderedTasksText) {
|
|
6199
|
+
updates.push({
|
|
6200
|
+
elementId: "streaming_tasks",
|
|
6201
|
+
content: tasksText,
|
|
6202
|
+
onSuccess: () => {
|
|
6203
|
+
state.renderedTasksText = tasksText;
|
|
6204
|
+
}
|
|
6205
|
+
});
|
|
6206
|
+
}
|
|
6038
6207
|
if (toolsText !== state.renderedToolsText) {
|
|
6039
6208
|
updates.push({
|
|
6040
6209
|
elementId: "streaming_tools",
|
|
@@ -6058,13 +6227,14 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
6058
6227
|
for (const update of updates) {
|
|
6059
6228
|
state.sequence++;
|
|
6060
6229
|
try {
|
|
6061
|
-
await cardkit.cardElement.content({
|
|
6230
|
+
await this.withFeishuRequestTimeout(streamKey, `cardElement.content:${update.elementId}`, () => cardkit.cardElement.content({
|
|
6062
6231
|
path: { card_id: cardId, element_id: update.elementId },
|
|
6063
6232
|
data: { content: update.content, sequence: state.sequence }
|
|
6064
|
-
});
|
|
6233
|
+
}));
|
|
6065
6234
|
update.onSuccess();
|
|
6066
|
-
|
|
6235
|
+
this.markCardFlushSuccess(state);
|
|
6067
6236
|
} catch (err) {
|
|
6237
|
+
this.markCardFlushFailure(state, err);
|
|
6068
6238
|
console.warn(
|
|
6069
6239
|
`[feishu-adapter] cardElement.content failed for ${update.elementId}:`,
|
|
6070
6240
|
err instanceof Error ? err.message : err
|
|
@@ -6125,13 +6295,13 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
6125
6295
|
await this.awaitCardFlushCompletion(cardKey);
|
|
6126
6296
|
try {
|
|
6127
6297
|
state.sequence++;
|
|
6128
|
-
await cardkit.card.settings({
|
|
6298
|
+
await this.withFeishuRequestTimeout(cardKey, "card.settings", () => cardkit.card.settings({
|
|
6129
6299
|
path: { card_id: state.cardId },
|
|
6130
6300
|
data: {
|
|
6131
6301
|
settings: JSON.stringify({ streaming_mode: false }),
|
|
6132
6302
|
sequence: state.sequence
|
|
6133
6303
|
}
|
|
6134
|
-
});
|
|
6304
|
+
}));
|
|
6135
6305
|
const statusLabels = {
|
|
6136
6306
|
completed: "\u2705 Completed",
|
|
6137
6307
|
interrupted: "\u26A0\uFE0F Interrupted",
|
|
@@ -6151,15 +6321,15 @@ var FeishuAdapter = class extends BaseChannelAdapter {
|
|
|
6151
6321
|
|
|
6152
6322
|
${trimmedResponse}`;
|
|
6153
6323
|
}
|
|
6154
|
-
const finalCardJson = buildFinalCardJson(finalText, state.toolCalls, footer);
|
|
6324
|
+
const finalCardJson = buildFinalCardJson(finalText, state.taskItems, state.toolCalls, footer);
|
|
6155
6325
|
state.sequence++;
|
|
6156
|
-
await cardkit.card.update({
|
|
6326
|
+
await this.withFeishuRequestTimeout(cardKey, "card.update", () => cardkit.card.update({
|
|
6157
6327
|
path: { card_id: state.cardId },
|
|
6158
6328
|
data: {
|
|
6159
6329
|
card: { type: "card_json", data: finalCardJson },
|
|
6160
6330
|
sequence: state.sequence
|
|
6161
6331
|
}
|
|
6162
|
-
});
|
|
6332
|
+
}));
|
|
6163
6333
|
console.log(`[feishu-adapter] Card finalized: streamKey=${cardKey}, cardId=${state.cardId}, status=${status}, elapsed=${formatElapsed(elapsedMs)}`);
|
|
6164
6334
|
return true;
|
|
6165
6335
|
} catch (err) {
|
|
@@ -6191,6 +6361,76 @@ ${trimmedResponse}`;
|
|
|
6191
6361
|
hasActiveStreamingUi(chatId, streamKey) {
|
|
6192
6362
|
return this.hasActiveCard(chatId, streamKey);
|
|
6193
6363
|
}
|
|
6364
|
+
getStructuredStreamingUiSnapshot(chatId, streamKey) {
|
|
6365
|
+
const state = this.activeCards.get(this.resolveStreamKey(chatId, streamKey));
|
|
6366
|
+
if (!state) return null;
|
|
6367
|
+
return {
|
|
6368
|
+
active: true,
|
|
6369
|
+
lastAttemptAt: state.lastFlushStartedAt,
|
|
6370
|
+
lastUpdateAt: state.lastSuccessfulFlushAt ?? (state.lastUpdateAt > 0 ? state.lastUpdateAt : null),
|
|
6371
|
+
lastErrorAt: state.lastFlushErrorAt,
|
|
6372
|
+
lastError: state.lastFlushError,
|
|
6373
|
+
flushInFlight: Boolean(state.flushInFlight),
|
|
6374
|
+
flushInFlightSince: state.flushInFlight ? state.lastFlushStartedAt : null,
|
|
6375
|
+
consecutiveFailures: state.consecutiveFlushFailures
|
|
6376
|
+
};
|
|
6377
|
+
}
|
|
6378
|
+
getCardRequestTimeoutMs() {
|
|
6379
|
+
return Math.max(1, this.cardRequestTimeoutMs);
|
|
6380
|
+
}
|
|
6381
|
+
logRequestOperation(phase, scope, target, startedAt, detail) {
|
|
6382
|
+
const durationMs = Math.max(0, Date.now() - startedAt);
|
|
6383
|
+
const suffix = detail ? `, detail=${detail}` : "";
|
|
6384
|
+
const line = `[feishu-adapter] Request ${phase}: scope=${scope}, target=${target}, duration=${durationMs}ms${suffix}`;
|
|
6385
|
+
if (phase === "start" || phase === "success") {
|
|
6386
|
+
console.log(line);
|
|
6387
|
+
return;
|
|
6388
|
+
}
|
|
6389
|
+
console.warn(line);
|
|
6390
|
+
}
|
|
6391
|
+
async withFeishuRequestTimeout(scope, target, operation) {
|
|
6392
|
+
const startedAt = Date.now();
|
|
6393
|
+
const timeoutMs = this.getCardRequestTimeoutMs();
|
|
6394
|
+
this.logRequestOperation("start", scope, target, startedAt);
|
|
6395
|
+
let timeoutHandle = null;
|
|
6396
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
6397
|
+
timeoutHandle = setTimeout(() => {
|
|
6398
|
+
reject(new Error(`timeout after ${timeoutMs}ms`));
|
|
6399
|
+
}, timeoutMs);
|
|
6400
|
+
});
|
|
6401
|
+
const operationPromise = operation();
|
|
6402
|
+
operationPromise.catch(() => {
|
|
6403
|
+
});
|
|
6404
|
+
try {
|
|
6405
|
+
const result = await Promise.race([operationPromise, timeoutPromise]);
|
|
6406
|
+
this.logRequestOperation("success", scope, target, startedAt);
|
|
6407
|
+
return result;
|
|
6408
|
+
} catch (error) {
|
|
6409
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
6410
|
+
this.logRequestOperation(
|
|
6411
|
+
detail.startsWith("timeout after ") ? "timeout" : "error",
|
|
6412
|
+
scope,
|
|
6413
|
+
target,
|
|
6414
|
+
startedAt,
|
|
6415
|
+
detail
|
|
6416
|
+
);
|
|
6417
|
+
throw error;
|
|
6418
|
+
} finally {
|
|
6419
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
6420
|
+
}
|
|
6421
|
+
}
|
|
6422
|
+
markCardFlushFailure(state, error) {
|
|
6423
|
+
state.lastFlushErrorAt = Date.now();
|
|
6424
|
+
state.lastFlushError = error instanceof Error ? error.message : String(error);
|
|
6425
|
+
state.consecutiveFlushFailures += 1;
|
|
6426
|
+
}
|
|
6427
|
+
markCardFlushSuccess(state) {
|
|
6428
|
+
const now2 = Date.now();
|
|
6429
|
+
state.lastUpdateAt = now2;
|
|
6430
|
+
state.lastSuccessfulFlushAt = now2;
|
|
6431
|
+
state.lastFlushError = null;
|
|
6432
|
+
state.consecutiveFlushFailures = 0;
|
|
6433
|
+
}
|
|
6194
6434
|
// ── Streaming adapter interface ────────────────────────────────
|
|
6195
6435
|
/**
|
|
6196
6436
|
* Called by bridge-manager on each text SSE event.
|
|
@@ -6218,8 +6458,30 @@ ${trimmedResponse}`;
|
|
|
6218
6458
|
}
|
|
6219
6459
|
onToolEvent(chatId, tools, streamKey) {
|
|
6220
6460
|
if (!this.isStreamingEnabled()) return;
|
|
6461
|
+
const cardKey = this.resolveStreamKey(chatId, streamKey);
|
|
6462
|
+
if (!this.activeCards.has(cardKey)) {
|
|
6463
|
+
const messageId = this.lastIncomingMessageId.get(chatId);
|
|
6464
|
+
this.createStreamingCard(chatId, messageId, cardKey).then((ok) => {
|
|
6465
|
+
if (ok) this.updateToolProgress(chatId, tools, cardKey);
|
|
6466
|
+
}).catch(() => {
|
|
6467
|
+
});
|
|
6468
|
+
return;
|
|
6469
|
+
}
|
|
6221
6470
|
this.updateToolProgress(chatId, tools, streamKey);
|
|
6222
6471
|
}
|
|
6472
|
+
onTaskEvent(chatId, tasks, streamKey) {
|
|
6473
|
+
if (!this.isStreamingEnabled()) return;
|
|
6474
|
+
const cardKey = this.resolveStreamKey(chatId, streamKey);
|
|
6475
|
+
if (!this.activeCards.has(cardKey)) {
|
|
6476
|
+
const messageId = this.lastIncomingMessageId.get(chatId);
|
|
6477
|
+
this.createStreamingCard(chatId, messageId, cardKey).then((ok) => {
|
|
6478
|
+
if (ok) this.updateTaskProgress(chatId, tasks, cardKey);
|
|
6479
|
+
}).catch(() => {
|
|
6480
|
+
});
|
|
6481
|
+
return;
|
|
6482
|
+
}
|
|
6483
|
+
this.updateTaskProgress(chatId, tasks, streamKey);
|
|
6484
|
+
}
|
|
6223
6485
|
onStreamStatus(chatId, statusText, streamKey) {
|
|
6224
6486
|
if (!this.isStreamingEnabled()) return;
|
|
6225
6487
|
const cardKey = this.resolveStreamKey(chatId, streamKey);
|
|
@@ -6369,17 +6631,17 @@ ${trimmedResponse}`;
|
|
|
6369
6631
|
}
|
|
6370
6632
|
async sendStructuredMessage(chatId, msgType, content, replyToMessageId) {
|
|
6371
6633
|
try {
|
|
6372
|
-
const res = replyToMessageId ? await this.restClient.im.message.reply({
|
|
6634
|
+
const res = replyToMessageId ? await this.withFeishuRequestTimeout(chatId, `im.message.reply:${msgType}`, () => this.restClient.im.message.reply({
|
|
6373
6635
|
path: { message_id: replyToMessageId },
|
|
6374
6636
|
data: { msg_type: msgType, content }
|
|
6375
|
-
}) : await this.restClient.im.message.create({
|
|
6637
|
+
})) : await this.withFeishuRequestTimeout(chatId, `im.message.create:${msgType}`, () => this.restClient.im.message.create({
|
|
6376
6638
|
params: { receive_id_type: "chat_id" },
|
|
6377
6639
|
data: {
|
|
6378
6640
|
receive_id: chatId,
|
|
6379
6641
|
msg_type: msgType,
|
|
6380
6642
|
content
|
|
6381
6643
|
}
|
|
6382
|
-
});
|
|
6644
|
+
}));
|
|
6383
6645
|
if (res?.data?.message_id) {
|
|
6384
6646
|
return { ok: true, messageId: res.data.message_id };
|
|
6385
6647
|
}
|
|
@@ -6395,14 +6657,14 @@ ${trimmedResponse}`;
|
|
|
6395
6657
|
async sendAsCard(chatId, text2) {
|
|
6396
6658
|
const cardContent = buildCardContent(text2);
|
|
6397
6659
|
try {
|
|
6398
|
-
const res = await this.restClient.im.message.create({
|
|
6660
|
+
const res = await this.withFeishuRequestTimeout(chatId, "im.message.create:interactive-card", () => this.restClient.im.message.create({
|
|
6399
6661
|
params: { receive_id_type: "chat_id" },
|
|
6400
6662
|
data: {
|
|
6401
6663
|
receive_id: chatId,
|
|
6402
6664
|
msg_type: "interactive",
|
|
6403
6665
|
content: cardContent
|
|
6404
6666
|
}
|
|
6405
|
-
});
|
|
6667
|
+
}));
|
|
6406
6668
|
if (res?.data?.message_id) {
|
|
6407
6669
|
return { ok: true, messageId: res.data.message_id };
|
|
6408
6670
|
}
|
|
@@ -6419,14 +6681,14 @@ ${trimmedResponse}`;
|
|
|
6419
6681
|
async sendAsPost(chatId, text2) {
|
|
6420
6682
|
const postContent = buildPostContent(text2);
|
|
6421
6683
|
try {
|
|
6422
|
-
const res = await this.restClient.im.message.create({
|
|
6684
|
+
const res = await this.withFeishuRequestTimeout(chatId, "im.message.create:post", () => this.restClient.im.message.create({
|
|
6423
6685
|
params: { receive_id_type: "chat_id" },
|
|
6424
6686
|
data: {
|
|
6425
6687
|
receive_id: chatId,
|
|
6426
6688
|
msg_type: "post",
|
|
6427
6689
|
content: postContent
|
|
6428
6690
|
}
|
|
6429
|
-
});
|
|
6691
|
+
}));
|
|
6430
6692
|
if (res?.data?.message_id) {
|
|
6431
6693
|
return { ok: true, messageId: res.data.message_id };
|
|
6432
6694
|
}
|
|
@@ -6438,14 +6700,14 @@ ${trimmedResponse}`;
|
|
|
6438
6700
|
}
|
|
6439
6701
|
async sendAsPlainText(chatId, text2) {
|
|
6440
6702
|
try {
|
|
6441
|
-
const res = await this.restClient.im.message.create({
|
|
6703
|
+
const res = await this.withFeishuRequestTimeout(chatId, "im.message.create:text", () => this.restClient.im.message.create({
|
|
6442
6704
|
params: { receive_id_type: "chat_id" },
|
|
6443
6705
|
data: {
|
|
6444
6706
|
receive_id: chatId,
|
|
6445
6707
|
msg_type: "text",
|
|
6446
6708
|
content: JSON.stringify({ text: text2 })
|
|
6447
6709
|
}
|
|
6448
|
-
});
|
|
6710
|
+
}));
|
|
6449
6711
|
if (res?.data?.message_id) {
|
|
6450
6712
|
return { ok: true, messageId: res.data.message_id };
|
|
6451
6713
|
}
|
|
@@ -6470,14 +6732,14 @@ ${trimmedResponse}`;
|
|
|
6470
6732
|
if (permId) {
|
|
6471
6733
|
const cardJson2 = buildPermissionButtonCard(mdText, permId, chatId);
|
|
6472
6734
|
try {
|
|
6473
|
-
const res = await this.restClient.im.message.create({
|
|
6735
|
+
const res = await this.withFeishuRequestTimeout(chatId, "im.message.create:permission-button-card", () => this.restClient.im.message.create({
|
|
6474
6736
|
params: { receive_id_type: "chat_id" },
|
|
6475
6737
|
data: {
|
|
6476
6738
|
receive_id: chatId,
|
|
6477
6739
|
msg_type: "interactive",
|
|
6478
6740
|
content: cardJson2
|
|
6479
6741
|
}
|
|
6480
|
-
});
|
|
6742
|
+
}));
|
|
6481
6743
|
if (res?.data?.message_id) {
|
|
6482
6744
|
return { ok: true, messageId: res.data.message_id };
|
|
6483
6745
|
}
|
|
@@ -6521,14 +6783,14 @@ ${trimmedResponse}`;
|
|
|
6521
6783
|
}
|
|
6522
6784
|
});
|
|
6523
6785
|
try {
|
|
6524
|
-
const res = await this.restClient.im.message.create({
|
|
6786
|
+
const res = await this.withFeishuRequestTimeout(chatId, "im.message.create:permission-fallback-card", () => this.restClient.im.message.create({
|
|
6525
6787
|
params: { receive_id_type: "chat_id" },
|
|
6526
6788
|
data: {
|
|
6527
6789
|
receive_id: chatId,
|
|
6528
6790
|
msg_type: "interactive",
|
|
6529
6791
|
content: cardJson
|
|
6530
6792
|
}
|
|
6531
|
-
});
|
|
6793
|
+
}));
|
|
6532
6794
|
if (res?.data?.message_id) {
|
|
6533
6795
|
return { ok: true, messageId: res.data.message_id };
|
|
6534
6796
|
}
|
|
@@ -6545,14 +6807,14 @@ ${trimmedResponse}`;
|
|
|
6545
6807
|
...permCommands
|
|
6546
6808
|
].join("\n");
|
|
6547
6809
|
try {
|
|
6548
|
-
const res = await this.restClient.im.message.create({
|
|
6810
|
+
const res = await this.withFeishuRequestTimeout(chatId, "im.message.create:permission-fallback-text", () => this.restClient.im.message.create({
|
|
6549
6811
|
params: { receive_id_type: "chat_id" },
|
|
6550
6812
|
data: {
|
|
6551
6813
|
receive_id: chatId,
|
|
6552
6814
|
msg_type: "text",
|
|
6553
6815
|
content: JSON.stringify({ text: plainText })
|
|
6554
6816
|
}
|
|
6555
|
-
});
|
|
6817
|
+
}));
|
|
6556
6818
|
if (res?.data?.message_id) {
|
|
6557
6819
|
return { ok: true, messageId: res.data.message_id };
|
|
6558
6820
|
}
|
|
@@ -15028,6 +15290,54 @@ function extractNormalizedStructuredText(value) {
|
|
|
15028
15290
|
collectStructuredTextParts(value, parts);
|
|
15029
15291
|
return parts.length > 0 ? normalizeStructuredText(parts.join("\n\n")) : "";
|
|
15030
15292
|
}
|
|
15293
|
+
function parseJsonSafely(value) {
|
|
15294
|
+
if (!value) return null;
|
|
15295
|
+
try {
|
|
15296
|
+
return JSON.parse(value);
|
|
15297
|
+
} catch {
|
|
15298
|
+
return null;
|
|
15299
|
+
}
|
|
15300
|
+
}
|
|
15301
|
+
function normalizeTaskStatus(value) {
|
|
15302
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
15303
|
+
if (normalized === "in_progress" || normalized === "running" || normalized === "active") {
|
|
15304
|
+
return "in_progress";
|
|
15305
|
+
}
|
|
15306
|
+
if (normalized === "completed" || normalized === "complete" || normalized === "done") {
|
|
15307
|
+
return "completed";
|
|
15308
|
+
}
|
|
15309
|
+
return "pending";
|
|
15310
|
+
}
|
|
15311
|
+
function parseTaskProgressItems(value) {
|
|
15312
|
+
if (!Array.isArray(value)) return [];
|
|
15313
|
+
return value.map((item) => {
|
|
15314
|
+
const record = item;
|
|
15315
|
+
const text2 = extractNormalizedStructuredText(record.text ?? record.step);
|
|
15316
|
+
if (!text2) return null;
|
|
15317
|
+
return {
|
|
15318
|
+
text: text2,
|
|
15319
|
+
status: normalizeTaskStatus(record.status)
|
|
15320
|
+
};
|
|
15321
|
+
}).filter((item) => Boolean(item));
|
|
15322
|
+
}
|
|
15323
|
+
function parseUpdatePlanTasks(argumentsJson) {
|
|
15324
|
+
const parsed = parseJsonSafely(argumentsJson);
|
|
15325
|
+
if (!parsed || typeof parsed !== "object") return [];
|
|
15326
|
+
return parseTaskProgressItems(parsed.plan ?? parsed.tasks);
|
|
15327
|
+
}
|
|
15328
|
+
function extractToolOutputText(value) {
|
|
15329
|
+
if (typeof value !== "string") return extractNormalizedFreeText(value);
|
|
15330
|
+
const trimmed = value.trim();
|
|
15331
|
+
if (!trimmed) return "";
|
|
15332
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
15333
|
+
const parsed = parseJsonSafely(trimmed);
|
|
15334
|
+
if (parsed && typeof parsed === "object") {
|
|
15335
|
+
const extracted = extractNormalizedFreeText(parsed.output ?? parsed);
|
|
15336
|
+
if (extracted) return extracted;
|
|
15337
|
+
}
|
|
15338
|
+
}
|
|
15339
|
+
return extractNormalizedFreeText(value);
|
|
15340
|
+
}
|
|
15031
15341
|
function createDesktopEventSignature(rawLine) {
|
|
15032
15342
|
return crypto7.createHash("sha1").update(rawLine).digest("hex");
|
|
15033
15343
|
}
|
|
@@ -15059,6 +15369,17 @@ function isSessionMessageLine(line) {
|
|
|
15059
15369
|
function isTurnContextLine(line) {
|
|
15060
15370
|
return line.type === "turn_context";
|
|
15061
15371
|
}
|
|
15372
|
+
function describeUnhandledMirrorLineKind(line) {
|
|
15373
|
+
if (isSessionEventLine(line)) {
|
|
15374
|
+
const payloadType = typeof line.payload?.type === "string" ? line.payload.type.trim() : "";
|
|
15375
|
+
return `event_msg:${payloadType || "<unknown>"}`;
|
|
15376
|
+
}
|
|
15377
|
+
if (isSessionMessageLine(line)) {
|
|
15378
|
+
const payloadType = typeof line.payload?.type === "string" ? line.payload.type.trim() : "";
|
|
15379
|
+
return `response_item:${payloadType || "<unknown>"}`;
|
|
15380
|
+
}
|
|
15381
|
+
return null;
|
|
15382
|
+
}
|
|
15062
15383
|
function listDesktopSessions(limit) {
|
|
15063
15384
|
const root = getCodexSessionsRoot();
|
|
15064
15385
|
if (!fs4.existsSync(root)) return [];
|
|
@@ -15151,81 +15472,213 @@ function pushDesktopSessionEvent(events, parsed, rawLine) {
|
|
|
15151
15472
|
});
|
|
15152
15473
|
}
|
|
15153
15474
|
}
|
|
15154
|
-
function pushDesktopMirrorRecord(records, parsed, rawLine, activeTurnId) {
|
|
15155
|
-
if (isSessionEventLine(parsed)
|
|
15475
|
+
function pushDesktopMirrorRecord(records, parsed, rawLine, activeTurnId, activeSpecialCallIds) {
|
|
15476
|
+
if (isSessionEventLine(parsed)) {
|
|
15477
|
+
return pushDesktopMirrorEventRecord(records, parsed, rawLine, activeTurnId);
|
|
15478
|
+
}
|
|
15479
|
+
if (isSessionMessageLine(parsed)) {
|
|
15480
|
+
return pushDesktopMirrorResponseRecord(records, parsed, rawLine, activeTurnId, activeSpecialCallIds);
|
|
15481
|
+
}
|
|
15482
|
+
return false;
|
|
15483
|
+
}
|
|
15484
|
+
function pushDesktopMirrorEventRecord(records, parsed, rawLine, activeTurnId) {
|
|
15485
|
+
const signature = createDesktopEventSignature(rawLine);
|
|
15486
|
+
const timestamp = parsed.timestamp || "";
|
|
15487
|
+
if (parsed.payload?.type === "task_started") {
|
|
15156
15488
|
records.push({
|
|
15157
|
-
signature
|
|
15489
|
+
signature,
|
|
15158
15490
|
type: "task_started",
|
|
15159
15491
|
content: "",
|
|
15160
|
-
timestamp
|
|
15492
|
+
timestamp,
|
|
15161
15493
|
turnId: parsed.payload.turn_id || ""
|
|
15162
15494
|
});
|
|
15163
|
-
return;
|
|
15495
|
+
return true;
|
|
15164
15496
|
}
|
|
15165
|
-
if (
|
|
15497
|
+
if (parsed.payload?.type === "turn_aborted") {
|
|
15498
|
+
records.push({
|
|
15499
|
+
signature,
|
|
15500
|
+
type: "task_aborted",
|
|
15501
|
+
content: extractNormalizedStructuredText(parsed.payload.reason),
|
|
15502
|
+
timestamp,
|
|
15503
|
+
...activeTurnId ? { turnId: activeTurnId } : {}
|
|
15504
|
+
});
|
|
15505
|
+
return true;
|
|
15506
|
+
}
|
|
15507
|
+
if (parsed.payload?.type === "agent_reasoning") {
|
|
15508
|
+
const text2 = extractNormalizedStructuredText(parsed.payload.text);
|
|
15509
|
+
if (!text2) return true;
|
|
15510
|
+
records.push({
|
|
15511
|
+
signature,
|
|
15512
|
+
type: "reasoning",
|
|
15513
|
+
content: text2,
|
|
15514
|
+
timestamp,
|
|
15515
|
+
...activeTurnId ? { turnId: activeTurnId } : {}
|
|
15516
|
+
});
|
|
15517
|
+
return true;
|
|
15518
|
+
}
|
|
15519
|
+
if (parsed.payload?.type === "web_search_end") {
|
|
15520
|
+
const toolId = extractNormalizedFreeText(parsed.payload.call_id) || signature;
|
|
15521
|
+
records.push({
|
|
15522
|
+
signature,
|
|
15523
|
+
type: "tool_finished",
|
|
15524
|
+
content: extractNormalizedStructuredText(parsed.payload.query),
|
|
15525
|
+
timestamp,
|
|
15526
|
+
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15527
|
+
toolId,
|
|
15528
|
+
toolName: "Web Search"
|
|
15529
|
+
});
|
|
15530
|
+
return true;
|
|
15531
|
+
}
|
|
15532
|
+
if (parsed.payload?.type === "mcp_tool_call_end") {
|
|
15533
|
+
const toolId = extractNormalizedFreeText(parsed.payload.call_id) || signature;
|
|
15534
|
+
const server = extractNormalizedFreeText(parsed.payload.invocation?.server);
|
|
15535
|
+
const tool = extractNormalizedFreeText(parsed.payload.invocation?.tool);
|
|
15536
|
+
const toolName = server && tool ? `mcp__${server}__${tool}` : "mcp_tool_call";
|
|
15537
|
+
records.push({
|
|
15538
|
+
signature,
|
|
15539
|
+
type: "tool_finished",
|
|
15540
|
+
content: "",
|
|
15541
|
+
timestamp,
|
|
15542
|
+
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15543
|
+
toolId,
|
|
15544
|
+
toolName,
|
|
15545
|
+
isError: false
|
|
15546
|
+
});
|
|
15547
|
+
return true;
|
|
15548
|
+
}
|
|
15549
|
+
if (parsed.payload?.type === "user_message") {
|
|
15166
15550
|
const text2 = extractNormalizedStructuredText(parsed.payload.message);
|
|
15167
|
-
if (!text2) return;
|
|
15551
|
+
if (!text2) return true;
|
|
15168
15552
|
records.push({
|
|
15169
|
-
signature
|
|
15553
|
+
signature,
|
|
15170
15554
|
type: "message",
|
|
15171
15555
|
role: "user",
|
|
15172
15556
|
content: text2,
|
|
15173
|
-
timestamp
|
|
15557
|
+
timestamp,
|
|
15174
15558
|
...activeTurnId ? { turnId: activeTurnId } : {}
|
|
15175
15559
|
});
|
|
15176
|
-
return;
|
|
15560
|
+
return true;
|
|
15177
15561
|
}
|
|
15178
|
-
if (
|
|
15562
|
+
if (parsed.payload?.type === "task_complete") {
|
|
15179
15563
|
records.push({
|
|
15180
|
-
signature
|
|
15564
|
+
signature,
|
|
15181
15565
|
type: "task_complete",
|
|
15182
15566
|
role: "assistant",
|
|
15183
15567
|
content: extractNormalizedStructuredText(parsed.payload.last_agent_message),
|
|
15184
|
-
timestamp
|
|
15568
|
+
timestamp,
|
|
15185
15569
|
turnId: parsed.payload.turn_id || ""
|
|
15186
15570
|
});
|
|
15187
|
-
return;
|
|
15571
|
+
return true;
|
|
15188
15572
|
}
|
|
15189
|
-
|
|
15573
|
+
return false;
|
|
15574
|
+
}
|
|
15575
|
+
function pushDesktopMirrorResponseRecord(records, parsed, rawLine, activeTurnId, activeSpecialCallIds) {
|
|
15576
|
+
const signature = createDesktopEventSignature(rawLine);
|
|
15577
|
+
const timestamp = parsed.timestamp || "";
|
|
15578
|
+
if (parsed.payload?.type === "message" && parsed.payload.role === "assistant") {
|
|
15190
15579
|
const text2 = extractDesktopMessageText(parsed);
|
|
15191
|
-
if (!text2) return;
|
|
15580
|
+
if (!text2) return true;
|
|
15192
15581
|
records.push({
|
|
15193
|
-
signature
|
|
15582
|
+
signature,
|
|
15194
15583
|
type: "message",
|
|
15195
15584
|
role: parsed.payload.phase === "commentary" ? "commentary" : "assistant",
|
|
15196
15585
|
content: parsed.payload.phase === "commentary" ? text2.replace(/^\[commentary\]\n/, "") : text2,
|
|
15197
|
-
timestamp
|
|
15586
|
+
timestamp,
|
|
15198
15587
|
...activeTurnId ? { turnId: activeTurnId } : {}
|
|
15199
15588
|
});
|
|
15200
|
-
return;
|
|
15589
|
+
return true;
|
|
15201
15590
|
}
|
|
15202
|
-
if (
|
|
15591
|
+
if (parsed.payload?.type === "function_call") {
|
|
15203
15592
|
const toolName = extractNormalizedFreeText(parsed.payload.name);
|
|
15204
|
-
const toolId = extractNormalizedFreeText(parsed.payload.call_id) ||
|
|
15205
|
-
if (!toolName) return;
|
|
15593
|
+
const toolId = extractNormalizedFreeText(parsed.payload.call_id) || signature;
|
|
15594
|
+
if (!toolName) return true;
|
|
15595
|
+
if (toolName === "update_plan") {
|
|
15596
|
+
const tasks = parseUpdatePlanTasks(parsed.payload.arguments);
|
|
15597
|
+
activeSpecialCallIds.add(toolId);
|
|
15598
|
+
records.push({
|
|
15599
|
+
signature,
|
|
15600
|
+
type: "plan_update",
|
|
15601
|
+
content: "",
|
|
15602
|
+
timestamp,
|
|
15603
|
+
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15604
|
+
tasks
|
|
15605
|
+
});
|
|
15606
|
+
return true;
|
|
15607
|
+
}
|
|
15206
15608
|
records.push({
|
|
15207
|
-
signature
|
|
15609
|
+
signature,
|
|
15208
15610
|
type: "tool_started",
|
|
15209
15611
|
content: "",
|
|
15210
|
-
timestamp
|
|
15612
|
+
timestamp,
|
|
15211
15613
|
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15212
15614
|
toolId,
|
|
15213
15615
|
toolName
|
|
15214
15616
|
});
|
|
15215
|
-
return;
|
|
15617
|
+
return true;
|
|
15216
15618
|
}
|
|
15217
|
-
if (
|
|
15218
|
-
const
|
|
15619
|
+
if (parsed.payload?.type === "custom_tool_call") {
|
|
15620
|
+
const toolName = extractNormalizedFreeText(parsed.payload.name);
|
|
15621
|
+
const toolId = extractNormalizedFreeText(parsed.payload.call_id) || signature;
|
|
15622
|
+
if (!toolName) return true;
|
|
15623
|
+
if (toolName === "update_plan") {
|
|
15624
|
+
const tasks = parseUpdatePlanTasks(typeof parsed.payload.input === "string" ? parsed.payload.input : void 0);
|
|
15625
|
+
activeSpecialCallIds.add(toolId);
|
|
15626
|
+
records.push({
|
|
15627
|
+
signature,
|
|
15628
|
+
type: "plan_update",
|
|
15629
|
+
content: "",
|
|
15630
|
+
timestamp,
|
|
15631
|
+
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15632
|
+
tasks
|
|
15633
|
+
});
|
|
15634
|
+
return true;
|
|
15635
|
+
}
|
|
15219
15636
|
records.push({
|
|
15220
|
-
signature
|
|
15637
|
+
signature,
|
|
15638
|
+
type: "tool_started",
|
|
15639
|
+
content: "",
|
|
15640
|
+
timestamp,
|
|
15641
|
+
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15642
|
+
toolId,
|
|
15643
|
+
toolName
|
|
15644
|
+
});
|
|
15645
|
+
return true;
|
|
15646
|
+
}
|
|
15647
|
+
if (parsed.payload?.type === "function_call_output") {
|
|
15648
|
+
const toolId = extractNormalizedFreeText(parsed.payload.call_id) || signature;
|
|
15649
|
+
if (activeSpecialCallIds.has(toolId)) {
|
|
15650
|
+
activeSpecialCallIds.delete(toolId);
|
|
15651
|
+
return true;
|
|
15652
|
+
}
|
|
15653
|
+
records.push({
|
|
15654
|
+
signature,
|
|
15655
|
+
type: "tool_finished",
|
|
15656
|
+
content: extractToolOutputText(parsed.payload.output),
|
|
15657
|
+
timestamp,
|
|
15658
|
+
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15659
|
+
toolId,
|
|
15660
|
+
isError: parsed.payload.is_error === true
|
|
15661
|
+
});
|
|
15662
|
+
return true;
|
|
15663
|
+
}
|
|
15664
|
+
if (parsed.payload?.type === "custom_tool_call_output") {
|
|
15665
|
+
const toolId = extractNormalizedFreeText(parsed.payload.call_id) || signature;
|
|
15666
|
+
if (activeSpecialCallIds.has(toolId)) {
|
|
15667
|
+
activeSpecialCallIds.delete(toolId);
|
|
15668
|
+
return true;
|
|
15669
|
+
}
|
|
15670
|
+
records.push({
|
|
15671
|
+
signature,
|
|
15221
15672
|
type: "tool_finished",
|
|
15222
|
-
content:
|
|
15223
|
-
timestamp
|
|
15673
|
+
content: extractToolOutputText(parsed.payload.output),
|
|
15674
|
+
timestamp,
|
|
15224
15675
|
...activeTurnId ? { turnId: activeTurnId } : {},
|
|
15225
15676
|
toolId,
|
|
15226
15677
|
isError: parsed.payload.is_error === true
|
|
15227
15678
|
});
|
|
15679
|
+
return true;
|
|
15228
15680
|
}
|
|
15681
|
+
return false;
|
|
15229
15682
|
}
|
|
15230
15683
|
function parseDesktopSessionEventText(content, leadingText = "", flushTrailingText = false) {
|
|
15231
15684
|
const combined = `${leadingText}${content}`;
|
|
@@ -15261,14 +15714,16 @@ function parseDesktopSessionEventText(content, leadingText = "", flushTrailingTe
|
|
|
15261
15714
|
trailingText
|
|
15262
15715
|
};
|
|
15263
15716
|
}
|
|
15264
|
-
function parseDesktopMirrorRecordText(content, leadingText = "", flushTrailingText = false, initialTurnId = null) {
|
|
15717
|
+
function parseDesktopMirrorRecordText(content, leadingText = "", flushTrailingText = false, initialTurnId = null, initialSpecialCallIds = []) {
|
|
15265
15718
|
const combined = `${leadingText}${content}`;
|
|
15266
15719
|
if (!combined) {
|
|
15267
15720
|
return {
|
|
15268
15721
|
records: [],
|
|
15269
15722
|
nextOffset: 0,
|
|
15270
15723
|
trailingText: "",
|
|
15271
|
-
nextTurnId: initialTurnId
|
|
15724
|
+
nextTurnId: initialTurnId,
|
|
15725
|
+
nextSpecialCallIds: [],
|
|
15726
|
+
unknownKinds: []
|
|
15272
15727
|
};
|
|
15273
15728
|
}
|
|
15274
15729
|
const hasTrailingNewline = combined.endsWith("\n") || combined.endsWith("\r");
|
|
@@ -15280,6 +15735,8 @@ function parseDesktopMirrorRecordText(content, leadingText = "", flushTrailingTe
|
|
|
15280
15735
|
}
|
|
15281
15736
|
const records = [];
|
|
15282
15737
|
let activeTurnId = initialTurnId;
|
|
15738
|
+
const activeSpecialCallIds = new Set(initialSpecialCallIds);
|
|
15739
|
+
const unknownKinds = /* @__PURE__ */ new Set();
|
|
15283
15740
|
for (const line of rawLines) {
|
|
15284
15741
|
const trimmed = line.trim();
|
|
15285
15742
|
if (!trimmed) continue;
|
|
@@ -15297,20 +15754,27 @@ function parseDesktopMirrorRecordText(content, leadingText = "", flushTrailingTe
|
|
|
15297
15754
|
const eventPayload = parsed.payload;
|
|
15298
15755
|
activeTurnId = eventPayload?.turn_id || activeTurnId;
|
|
15299
15756
|
}
|
|
15300
|
-
pushDesktopMirrorRecord(records, parsed, trimmed, activeTurnId);
|
|
15301
|
-
if (
|
|
15757
|
+
const handled = pushDesktopMirrorRecord(records, parsed, trimmed, activeTurnId, activeSpecialCallIds);
|
|
15758
|
+
if (!handled) {
|
|
15759
|
+
const unknownKind = describeUnhandledMirrorLineKind(parsed);
|
|
15760
|
+
if (unknownKind) unknownKinds.add(unknownKind);
|
|
15761
|
+
}
|
|
15762
|
+
if (isSessionEventLine(parsed) && (parsed.payload?.type === "task_complete" || parsed.payload?.type === "turn_aborted")) {
|
|
15302
15763
|
const eventPayload = parsed.payload;
|
|
15303
15764
|
const completedTurnId = eventPayload?.turn_id || activeTurnId;
|
|
15304
15765
|
if (!completedTurnId || completedTurnId === activeTurnId) {
|
|
15305
15766
|
activeTurnId = null;
|
|
15306
15767
|
}
|
|
15768
|
+
activeSpecialCallIds.clear();
|
|
15307
15769
|
}
|
|
15308
15770
|
}
|
|
15309
15771
|
return {
|
|
15310
15772
|
records,
|
|
15311
15773
|
nextOffset: 0,
|
|
15312
15774
|
trailingText,
|
|
15313
|
-
nextTurnId: activeTurnId
|
|
15775
|
+
nextTurnId: activeTurnId,
|
|
15776
|
+
nextSpecialCallIds: Array.from(activeSpecialCallIds),
|
|
15777
|
+
unknownKinds: Array.from(unknownKinds)
|
|
15314
15778
|
};
|
|
15315
15779
|
}
|
|
15316
15780
|
function readDesktopSessionMessages(threadId, limit = 8) {
|
|
@@ -15331,7 +15795,7 @@ function readDesktopSessionEventStreamByFilePath(filePath) {
|
|
|
15331
15795
|
}
|
|
15332
15796
|
return parseDesktopSessionEventText(content, "", true).events;
|
|
15333
15797
|
}
|
|
15334
|
-
function readDesktopSessionMirrorRecordDeltaByFilePath(filePath, startOffset, endOffset, trailingText = "", currentTurnId = null) {
|
|
15798
|
+
function readDesktopSessionMirrorRecordDeltaByFilePath(filePath, startOffset, endOffset, trailingText = "", currentTurnId = null, currentSpecialCallIds = []) {
|
|
15335
15799
|
let content = "";
|
|
15336
15800
|
try {
|
|
15337
15801
|
content = readFileUtf8Range(filePath, startOffset, endOffset);
|
|
@@ -15340,15 +15804,19 @@ function readDesktopSessionMirrorRecordDeltaByFilePath(filePath, startOffset, en
|
|
|
15340
15804
|
records: [],
|
|
15341
15805
|
nextOffset: startOffset,
|
|
15342
15806
|
trailingText,
|
|
15343
|
-
nextTurnId: currentTurnId
|
|
15807
|
+
nextTurnId: currentTurnId,
|
|
15808
|
+
nextSpecialCallIds: Array.from(currentSpecialCallIds),
|
|
15809
|
+
unknownKinds: []
|
|
15344
15810
|
};
|
|
15345
15811
|
}
|
|
15346
|
-
const parsed = parseDesktopMirrorRecordText(content, trailingText, false, currentTurnId);
|
|
15812
|
+
const parsed = parseDesktopMirrorRecordText(content, trailingText, false, currentTurnId, currentSpecialCallIds);
|
|
15347
15813
|
return {
|
|
15348
15814
|
records: parsed.records,
|
|
15349
15815
|
nextOffset: Math.max(startOffset, endOffset),
|
|
15350
15816
|
trailingText: parsed.trailingText,
|
|
15351
|
-
nextTurnId: parsed.nextTurnId
|
|
15817
|
+
nextTurnId: parsed.nextTurnId,
|
|
15818
|
+
nextSpecialCallIds: parsed.nextSpecialCallIds,
|
|
15819
|
+
unknownKinds: parsed.unknownKinds
|
|
15352
15820
|
};
|
|
15353
15821
|
}
|
|
15354
15822
|
function readDesktopSessionEventStream(threadId) {
|
|
@@ -16453,6 +16921,8 @@ function formatHealthStatusLabel(healthStatus) {
|
|
|
16453
16921
|
return "\u957F\u65F6\u8FD0\u884C\uFF0C\u5F85\u89C2\u5BDF";
|
|
16454
16922
|
case "suspected_stall":
|
|
16455
16923
|
return "\u7591\u4F3C\u5361\u4F4F";
|
|
16924
|
+
case "suspected_stream_ui_stall":
|
|
16925
|
+
return "\u6D41\u5F0F UI \u7591\u4F3C\u5361\u4F4F";
|
|
16456
16926
|
case "suspected_detached":
|
|
16457
16927
|
return "\u7591\u4F3C\u8131\u6302";
|
|
16458
16928
|
case "completed":
|
|
@@ -16507,8 +16977,10 @@ function buildHealthCommandResponse(title, diagnosis, markdown = false) {
|
|
|
16507
16977
|
["\u5065\u5EB7\u72B6\u6001", formatHealthStatusLabel(diagnosis.healthStatus)],
|
|
16508
16978
|
["\u5F53\u524D\u9636\u6BB5", currentStage],
|
|
16509
16979
|
["\u6700\u540E\u8FDB\u5C55", formatCommandTimestamp(diagnosis.lastProgressAt)],
|
|
16980
|
+
["\u6D41\u5F0F UI \u5237\u65B0", formatCommandTimestamp(diagnosis.lastStreamUiUpdateAt)],
|
|
16510
16981
|
["\u5DE5\u5177\u5F00\u59CB", formatCommandTimestamp(diagnosis.activeToolStartedAt)],
|
|
16511
16982
|
["\u6700\u8FD1\u5DE5\u5177\u5B8C\u6210", formatCommandTimestamp(diagnosis.lastToolFinishedAt)],
|
|
16983
|
+
["\u6D41\u5F0F UI \u9519\u8BEF", diagnosis.lastStreamUiErrorAt ? `${formatCommandTimestamp(diagnosis.lastStreamUiErrorAt)}${diagnosis.lastStreamUiError ? ` \xB7 ${diagnosis.lastStreamUiError}` : ""}` : "-"],
|
|
16512
16984
|
["\u672C\u5730\u8FDB\u7A0B", formatHealthProcessProbe(diagnosis)]
|
|
16513
16985
|
],
|
|
16514
16986
|
[diagnosis.healthReason],
|
|
@@ -16630,11 +17102,13 @@ function createMirrorTurnState(sessionId, timestamp, turnId) {
|
|
|
16630
17102
|
lastActivityAt: safeTimestamp,
|
|
16631
17103
|
lastStatusText: null,
|
|
16632
17104
|
lastStatusAt: 0,
|
|
17105
|
+
statusNote: null,
|
|
16633
17106
|
userText: null,
|
|
16634
17107
|
lastAssistantText: null,
|
|
16635
17108
|
lastCommentaryText: null,
|
|
16636
17109
|
streamedText: "",
|
|
16637
17110
|
streamStarted: false,
|
|
17111
|
+
taskItems: [],
|
|
16638
17112
|
toolCalls: /* @__PURE__ */ new Map()
|
|
16639
17113
|
};
|
|
16640
17114
|
}
|
|
@@ -16682,7 +17156,7 @@ function finalizeMirrorTurn(subscription, signature, timestamp, status, preferre
|
|
|
16682
17156
|
pendingTurn.lastCommentaryText
|
|
16683
17157
|
].map((value) => (value || "").trim()).find(Boolean) || "";
|
|
16684
17158
|
const userText = pendingTurn.userText?.trim() || null;
|
|
16685
|
-
if (!text2 && !userText && pendingTurn.toolCalls.size === 0) return null;
|
|
17159
|
+
if (!text2 && !userText && pendingTurn.toolCalls.size === 0 && pendingTurn.taskItems.length === 0) return null;
|
|
16686
17160
|
return {
|
|
16687
17161
|
streamKey: pendingTurn.streamKey,
|
|
16688
17162
|
userText,
|
|
@@ -16721,6 +17195,12 @@ function consumeMirrorRecords(subscription, records, hooks = {}) {
|
|
|
16721
17195
|
if (completed) finalized.push(completed);
|
|
16722
17196
|
continue;
|
|
16723
17197
|
}
|
|
17198
|
+
if (record.type === "task_aborted") {
|
|
17199
|
+
ensureMirrorTurnState(subscription, record);
|
|
17200
|
+
const interrupted = finalizeMirrorTurn(subscription, record.signature, record.timestamp, "interrupted");
|
|
17201
|
+
if (interrupted) finalized.push(interrupted);
|
|
17202
|
+
continue;
|
|
17203
|
+
}
|
|
16724
17204
|
if (record.type === "message" && record.role === "user") {
|
|
16725
17205
|
const pendingTurn = ensureMirrorTurnState(subscription, record);
|
|
16726
17206
|
const text2 = record.content.trim();
|
|
@@ -16749,6 +17229,20 @@ function consumeMirrorRecords(subscription, records, hooks = {}) {
|
|
|
16749
17229
|
}
|
|
16750
17230
|
continue;
|
|
16751
17231
|
}
|
|
17232
|
+
if (record.type === "reasoning") {
|
|
17233
|
+
const pendingTurn = ensureMirrorTurnState(subscription, record);
|
|
17234
|
+
const text2 = record.content.trim();
|
|
17235
|
+
if (!text2) continue;
|
|
17236
|
+
pendingTurn.statusNote = text2;
|
|
17237
|
+
hooks.onStatusProgress?.(subscription, pendingTurn);
|
|
17238
|
+
continue;
|
|
17239
|
+
}
|
|
17240
|
+
if (record.type === "plan_update") {
|
|
17241
|
+
const pendingTurn = ensureMirrorTurnState(subscription, record);
|
|
17242
|
+
pendingTurn.taskItems = record.tasks || [];
|
|
17243
|
+
hooks.onTaskProgress?.(subscription, pendingTurn);
|
|
17244
|
+
continue;
|
|
17245
|
+
}
|
|
16752
17246
|
if (record.type === "tool_started") {
|
|
16753
17247
|
const pendingTurn = ensureMirrorTurnState(subscription, record);
|
|
16754
17248
|
const toolId = record.toolId || record.signature;
|
|
@@ -16791,8 +17285,30 @@ function flushTimedOutMirrorTurn(subscription, idleTimeoutMs, nowMs = Date.now()
|
|
|
16791
17285
|
"interrupted"
|
|
16792
17286
|
);
|
|
16793
17287
|
}
|
|
17288
|
+
function enqueuePendingMirrorDeliveries(subscription, turns) {
|
|
17289
|
+
if (turns.length === 0) return;
|
|
17290
|
+
const existingSignatures = new Set(subscription.pendingDeliveries.map((turn) => turn.signature));
|
|
17291
|
+
for (const turn of turns) {
|
|
17292
|
+
if (existingSignatures.has(turn.signature)) continue;
|
|
17293
|
+
subscription.pendingDeliveries.push(turn);
|
|
17294
|
+
existingSignatures.add(turn.signature);
|
|
17295
|
+
}
|
|
17296
|
+
}
|
|
17297
|
+
function removePendingMirrorDeliveries(subscription, turns) {
|
|
17298
|
+
if (turns.length === 0 || subscription.pendingDeliveries.length === 0) return;
|
|
17299
|
+
const deliveredSignatures = new Set(turns.map((turn) => turn.signature));
|
|
17300
|
+
subscription.pendingDeliveries = subscription.pendingDeliveries.filter(
|
|
17301
|
+
(turn) => !deliveredSignatures.has(turn.signature)
|
|
17302
|
+
);
|
|
17303
|
+
}
|
|
17304
|
+
function selectPendingMirrorDeliveries(subscription, blocked) {
|
|
17305
|
+
if (!blocked) {
|
|
17306
|
+
return subscription.pendingDeliveries.slice();
|
|
17307
|
+
}
|
|
17308
|
+
return subscription.pendingDeliveries.filter((turn) => turn.timedOut);
|
|
17309
|
+
}
|
|
16794
17310
|
function hasPendingMirrorWork(subscription) {
|
|
16795
|
-
return subscription.bufferedRecords.length > 0 || subscription.pendingTurn !== null;
|
|
17311
|
+
return subscription.bufferedRecords.length > 0 || subscription.pendingTurn !== null || subscription.pendingDeliveries.length > 0;
|
|
16796
17312
|
}
|
|
16797
17313
|
function consumeBufferedMirrorTurns(subscription, idleTimeoutMs, nowMs = Date.now(), hooks = {}) {
|
|
16798
17314
|
const bufferedRecords = subscription.bufferedRecords;
|
|
@@ -18613,7 +19129,7 @@ function buildConversationPromptText(text2, files = []) {
|
|
|
18613
19129
|
|
|
18614
19130
|
${attachmentSupplement}` : attachmentSupplement;
|
|
18615
19131
|
}
|
|
18616
|
-
async function processMessage(binding, text2, onPermissionRequest, abortSignal, files, onPartialText, onToolEvent, onPromptPrepared) {
|
|
19132
|
+
async function processMessage(binding, text2, onPermissionRequest, abortSignal, files, onPartialText, onToolEvent, onTaskEvent, onStatusNote, onPromptPrepared) {
|
|
18617
19133
|
const { store, llm } = getBridgeContext();
|
|
18618
19134
|
const sessionId = binding.codepilotSessionId;
|
|
18619
19135
|
const lockId = crypto8.randomBytes(8).toString("hex");
|
|
@@ -18732,14 +19248,22 @@ async function processMessage(binding, text2, onPermissionRequest, abortSignal,
|
|
|
18732
19248
|
}
|
|
18733
19249
|
}
|
|
18734
19250
|
});
|
|
18735
|
-
return await consumeStream(
|
|
19251
|
+
return await consumeStream(
|
|
19252
|
+
stream,
|
|
19253
|
+
sessionId,
|
|
19254
|
+
onPermissionRequest,
|
|
19255
|
+
onPartialText,
|
|
19256
|
+
onToolEvent,
|
|
19257
|
+
onTaskEvent,
|
|
19258
|
+
onStatusNote
|
|
19259
|
+
);
|
|
18736
19260
|
} finally {
|
|
18737
19261
|
clearInterval(renewalInterval);
|
|
18738
19262
|
store.releaseSessionLock(sessionId, lockId);
|
|
18739
19263
|
store.setSessionRuntimeStatus(sessionId, "idle");
|
|
18740
19264
|
}
|
|
18741
19265
|
}
|
|
18742
|
-
async function consumeStream(stream, sessionId, onPermissionRequest, onPartialText, onToolEvent) {
|
|
19266
|
+
async function consumeStream(stream, sessionId, onPermissionRequest, onPartialText, onToolEvent, onTaskEvent, onStatusNote) {
|
|
18743
19267
|
const { store } = getBridgeContext();
|
|
18744
19268
|
const contentBlocks = [];
|
|
18745
19269
|
let currentText = "";
|
|
@@ -18848,6 +19372,12 @@ async function consumeStream(stream, sessionId, onPermissionRequest, onPartialTe
|
|
|
18848
19372
|
if (statusData.model) {
|
|
18849
19373
|
store.updateSessionModel(sessionId, statusData.model);
|
|
18850
19374
|
}
|
|
19375
|
+
if (typeof statusData.reasoning === "string" && onStatusNote) {
|
|
19376
|
+
try {
|
|
19377
|
+
onStatusNote(statusData.reasoning);
|
|
19378
|
+
} catch {
|
|
19379
|
+
}
|
|
19380
|
+
}
|
|
18851
19381
|
} catch {
|
|
18852
19382
|
}
|
|
18853
19383
|
break;
|
|
@@ -18855,8 +19385,15 @@ async function consumeStream(stream, sessionId, onPermissionRequest, onPartialTe
|
|
|
18855
19385
|
case "task_update": {
|
|
18856
19386
|
try {
|
|
18857
19387
|
const taskData = JSON.parse(event.data);
|
|
18858
|
-
|
|
18859
|
-
|
|
19388
|
+
const tasks = Array.isArray(taskData.tasks) ? taskData.tasks : Array.isArray(taskData.todos) ? taskData.todos : null;
|
|
19389
|
+
if (tasks) {
|
|
19390
|
+
store.syncSdkTasks(sessionId, tasks);
|
|
19391
|
+
if (onTaskEvent) {
|
|
19392
|
+
try {
|
|
19393
|
+
onTaskEvent(tasks);
|
|
19394
|
+
} catch {
|
|
19395
|
+
}
|
|
19396
|
+
}
|
|
18860
19397
|
}
|
|
18861
19398
|
} catch {
|
|
18862
19399
|
}
|
|
@@ -18954,6 +19491,14 @@ function pushStreamFeedbackTools(target, tools) {
|
|
|
18954
19491
|
} catch {
|
|
18955
19492
|
}
|
|
18956
19493
|
}
|
|
19494
|
+
function pushStreamFeedbackTasks(target, tasks) {
|
|
19495
|
+
if (typeof target.adapter.onTaskEvent !== "function") return;
|
|
19496
|
+
target.ensureStarted?.();
|
|
19497
|
+
try {
|
|
19498
|
+
target.adapter.onTaskEvent(target.chatId, tasks, target.streamKey);
|
|
19499
|
+
} catch {
|
|
19500
|
+
}
|
|
19501
|
+
}
|
|
18957
19502
|
function pushStreamFeedbackStatus(target, text2) {
|
|
18958
19503
|
if (typeof target.adapter.onStreamStatus !== "function") return;
|
|
18959
19504
|
target.ensureStarted?.();
|
|
@@ -19032,12 +19577,15 @@ function formatRuntimeDuration(ms) {
|
|
|
19032
19577
|
if (seconds === 0) return `${hours}h ${minutes}m`;
|
|
19033
19578
|
return `${hours}h ${minutes}m ${seconds}s`;
|
|
19034
19579
|
}
|
|
19035
|
-
function formatInteractiveRuntimeStatus(elapsedMs, silentMs) {
|
|
19580
|
+
function formatInteractiveRuntimeStatus(elapsedMs, silentMs, statusNote) {
|
|
19036
19581
|
const parts = [elapsedMs < 1e3 ? "\u5904\u7406\u4E2D" : `\u5DF2\u8FD0\u884C ${formatRuntimeDuration(elapsedMs)}`];
|
|
19037
19582
|
if (typeof silentMs === "number" && silentMs >= 0) {
|
|
19038
19583
|
parts.push(`\u6700\u8FD1 ${formatRuntimeDuration(silentMs)} \u65E0\u65B0\u8F93\u51FA`);
|
|
19039
19584
|
}
|
|
19040
|
-
|
|
19585
|
+
const runtimeText = parts.join("\uFF0C");
|
|
19586
|
+
const note = (statusNote || "").trim();
|
|
19587
|
+
return note ? `\u5F53\u524D\u6B65\u9AA4\uFF1A${note}
|
|
19588
|
+
${runtimeText}` : runtimeText;
|
|
19041
19589
|
}
|
|
19042
19590
|
async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
19043
19591
|
const binding = resolve(msg.address);
|
|
@@ -19132,23 +19680,35 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19132
19680
|
adapter,
|
|
19133
19681
|
channelType: adapter.channelType,
|
|
19134
19682
|
chatId: msg.address.chatId,
|
|
19135
|
-
streamKey
|
|
19683
|
+
streamKey,
|
|
19684
|
+
ensureStarted: () => {
|
|
19685
|
+
adapter.onMessageStart?.(msg.address.chatId, streamKey);
|
|
19686
|
+
}
|
|
19136
19687
|
};
|
|
19137
19688
|
const supportsPersistentStreamStatus = hasStreamingCards && adapter.provider === "feishu" && typeof adapter.onStreamStatus === "function";
|
|
19138
19689
|
const supportsStructuredStreamUi = supportsPersistentStreamStatus && (adapter.supportsStructuredStreamingUi?.(msg.address.chatId) ?? true);
|
|
19690
|
+
let latestStatusNote = null;
|
|
19691
|
+
let latestTasks = [];
|
|
19139
19692
|
const syncStructuredStreamUiState = () => {
|
|
19140
19693
|
if (!supportsStructuredStreamUi || taskState.structuredStreamUiActive) return;
|
|
19141
19694
|
if (adapter.hasActiveStreamingUi?.(msg.address.chatId, streamKey)) {
|
|
19142
19695
|
taskState.structuredStreamUiActive = true;
|
|
19143
19696
|
}
|
|
19144
19697
|
};
|
|
19698
|
+
const syncStructuredStreamUiSnapshot = () => {
|
|
19699
|
+
if (!supportsStructuredStreamUi) return;
|
|
19700
|
+
syncStructuredStreamUiState();
|
|
19701
|
+
const snapshot = adapter.getStructuredStreamingUiSnapshot?.(msg.address.chatId, streamKey);
|
|
19702
|
+
if (!snapshot) return;
|
|
19703
|
+
deps.recordInteractiveStreamUiSnapshot?.(binding.codepilotSessionId, snapshot);
|
|
19704
|
+
};
|
|
19145
19705
|
const pushRunningStatus = (silentMs) => {
|
|
19146
19706
|
if (!supportsStructuredStreamUi || streamStatusUpdatesClosed) return;
|
|
19147
19707
|
pushStreamFeedbackStatus(
|
|
19148
19708
|
streamFeedbackTarget,
|
|
19149
|
-
formatInteractiveRuntimeStatus(nowMs() - taskStartedAt, silentMs)
|
|
19709
|
+
formatInteractiveRuntimeStatus(nowMs() - taskStartedAt, silentMs, latestStatusNote)
|
|
19150
19710
|
);
|
|
19151
|
-
|
|
19711
|
+
syncStructuredStreamUiSnapshot();
|
|
19152
19712
|
};
|
|
19153
19713
|
let streamStatusHeartbeat = null;
|
|
19154
19714
|
let streamStatusUpdatesClosed = false;
|
|
@@ -19182,7 +19742,24 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19182
19742
|
pushStreamFeedbackTools(streamFeedbackTarget, Array.from(toolCallTracker.values()));
|
|
19183
19743
|
}
|
|
19184
19744
|
pushRunningStatus(null);
|
|
19185
|
-
|
|
19745
|
+
syncStructuredStreamUiSnapshot();
|
|
19746
|
+
};
|
|
19747
|
+
const onTaskEvent = (tasks) => {
|
|
19748
|
+
if (!deps.isCurrentInteractiveTask(binding.codepilotSessionId, taskId)) return;
|
|
19749
|
+
deps.touchInteractiveTask(binding.codepilotSessionId, taskId);
|
|
19750
|
+
latestTasks = tasks;
|
|
19751
|
+
if (hasStreamingCards) {
|
|
19752
|
+
pushStreamFeedbackTasks(streamFeedbackTarget, latestTasks);
|
|
19753
|
+
}
|
|
19754
|
+
pushRunningStatus(null);
|
|
19755
|
+
syncStructuredStreamUiSnapshot();
|
|
19756
|
+
};
|
|
19757
|
+
const onStatusNote = (note) => {
|
|
19758
|
+
if (!deps.isCurrentInteractiveTask(binding.codepilotSessionId, taskId)) return;
|
|
19759
|
+
deps.touchInteractiveTask(binding.codepilotSessionId, taskId);
|
|
19760
|
+
latestStatusNote = (note || "").trim() || null;
|
|
19761
|
+
pushRunningStatus(null);
|
|
19762
|
+
syncStructuredStreamUiSnapshot();
|
|
19186
19763
|
};
|
|
19187
19764
|
const onPartialText = (fullText) => {
|
|
19188
19765
|
if (!deps.isCurrentInteractiveTask(binding.codepilotSessionId, taskId)) return;
|
|
@@ -19191,7 +19768,7 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19191
19768
|
previewOnPartialText?.(fullText);
|
|
19192
19769
|
onStreamCardText?.(fullText);
|
|
19193
19770
|
pushRunningStatus(null);
|
|
19194
|
-
|
|
19771
|
+
syncStructuredStreamUiSnapshot();
|
|
19195
19772
|
};
|
|
19196
19773
|
if (supportsStructuredStreamUi) {
|
|
19197
19774
|
pushRunningStatus(null);
|
|
@@ -19205,11 +19782,10 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19205
19782
|
return;
|
|
19206
19783
|
}
|
|
19207
19784
|
const elapsedMs = nowMs() - taskStartedAt;
|
|
19208
|
-
if (elapsedMs < streamStatusIdleDetectionStartMs) return;
|
|
19209
19785
|
const silentMs = nowMs() - taskState.lastActivityAt;
|
|
19210
|
-
|
|
19211
|
-
pushRunningStatus(
|
|
19212
|
-
|
|
19786
|
+
const showSilentDuration = elapsedMs >= streamStatusIdleDetectionStartMs && silentMs >= streamStatusHeartbeatMs ? silentMs : null;
|
|
19787
|
+
pushRunningStatus(showSilentDuration);
|
|
19788
|
+
syncStructuredStreamUiSnapshot();
|
|
19213
19789
|
}, streamStatusHeartbeatMs);
|
|
19214
19790
|
}
|
|
19215
19791
|
let finalOutcome = "failed";
|
|
@@ -19236,11 +19812,16 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19236
19812
|
"permission_wait",
|
|
19237
19813
|
`\u5F53\u524D\u6B63\u5728\u7B49\u5F85\u5DE5\u5177 ${perm.toolName} \u7684\u6743\u9650\u786E\u8BA4\u3002`
|
|
19238
19814
|
);
|
|
19815
|
+
deps.touchInteractiveTask(binding.codepilotSessionId, taskId);
|
|
19816
|
+
pushRunningStatus(null);
|
|
19817
|
+
syncStructuredStreamUiSnapshot();
|
|
19239
19818
|
},
|
|
19240
19819
|
taskAbort.signal,
|
|
19241
19820
|
attachments && attachments.length > 0 ? attachments : void 0,
|
|
19242
19821
|
onPartialText,
|
|
19243
19822
|
onToolEvent,
|
|
19823
|
+
onTaskEvent,
|
|
19824
|
+
onStatusNote,
|
|
19244
19825
|
(preparedPrompt) => {
|
|
19245
19826
|
if (!taskState.mirrorSuppressionId) {
|
|
19246
19827
|
taskState.mirrorSuppressionId = deps.beginMirrorSuppression(binding.codepilotSessionId, preparedPrompt);
|
|
@@ -19263,14 +19844,16 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19263
19844
|
}
|
|
19264
19845
|
if (result.responseText || result.outboundAttachments.length > 0) {
|
|
19265
19846
|
const textToDeliver = cardFinalized ? "" : result.responseText;
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19847
|
+
if (!cardFinalized || result.outboundAttachments.length > 0) {
|
|
19848
|
+
await deps.deliverResponse(
|
|
19849
|
+
adapter,
|
|
19850
|
+
msg.address,
|
|
19851
|
+
textToDeliver,
|
|
19852
|
+
binding.codepilotSessionId,
|
|
19853
|
+
msg.messageId,
|
|
19854
|
+
result.outboundAttachments
|
|
19855
|
+
);
|
|
19856
|
+
}
|
|
19274
19857
|
} else if (result.hasError) {
|
|
19275
19858
|
await deps.deliverResponse(
|
|
19276
19859
|
adapter,
|
|
@@ -19289,6 +19872,7 @@ async function runInteractiveMessage(adapter, msg, text2, attachments, deps) {
|
|
|
19289
19872
|
finalOutcomeDetail = result.hasError ? result.errorMessage?.trim() || void 0 : void 0;
|
|
19290
19873
|
} finally {
|
|
19291
19874
|
stopStructuredStreamStatusUpdates();
|
|
19875
|
+
deps.recordInteractiveStreamUiSnapshot?.(binding.codepilotSessionId, { active: false });
|
|
19292
19876
|
if (previewState) {
|
|
19293
19877
|
if (previewState.throttleTimer) {
|
|
19294
19878
|
clearTimeout(previewState.throttleTimer);
|
|
@@ -19504,6 +20088,7 @@ function resetMirrorReadState(subscription) {
|
|
|
19504
20088
|
subscription.fileIdentity = null;
|
|
19505
20089
|
subscription.trailingText = "";
|
|
19506
20090
|
subscription.activeMirrorTurnId = null;
|
|
20091
|
+
subscription.activeSpecialCallIds.clear();
|
|
19507
20092
|
subscription.bufferedRecords = [];
|
|
19508
20093
|
}
|
|
19509
20094
|
function createMirrorSubscription(input) {
|
|
@@ -19527,8 +20112,11 @@ function createMirrorSubscription(input) {
|
|
|
19527
20112
|
fileIdentity: null,
|
|
19528
20113
|
trailingText: "",
|
|
19529
20114
|
activeMirrorTurnId: null,
|
|
20115
|
+
activeSpecialCallIds: /* @__PURE__ */ new Set(),
|
|
19530
20116
|
bufferedRecords: [],
|
|
19531
20117
|
pendingTurn: null,
|
|
20118
|
+
pendingDeliveries: [],
|
|
20119
|
+
unknownMirrorKindsSeen: /* @__PURE__ */ new Set(),
|
|
19532
20120
|
missingThreadPolls: 0,
|
|
19533
20121
|
consecutiveFailures: 0,
|
|
19534
20122
|
suspendedUntil: null
|
|
@@ -19539,6 +20127,8 @@ function resetMirrorSubscriptionForThreadChange(subscription, lastDeliveredAt) {
|
|
|
19539
20127
|
subscription.lastDeliveredAt = lastDeliveredAt;
|
|
19540
20128
|
subscription.dirty = true;
|
|
19541
20129
|
subscription.pendingTurn = null;
|
|
20130
|
+
subscription.pendingDeliveries = [];
|
|
20131
|
+
subscription.unknownMirrorKindsSeen.clear();
|
|
19542
20132
|
subscription.missingThreadPolls = 0;
|
|
19543
20133
|
subscription.consecutiveFailures = 0;
|
|
19544
20134
|
subscription.suspendedUntil = null;
|
|
@@ -19735,6 +20325,7 @@ function isMirrorSnapshotUnchanged(subscription, snapshot) {
|
|
|
19735
20325
|
}
|
|
19736
20326
|
function readMirrorDeliverableRecords(subscription, snapshot) {
|
|
19737
20327
|
let deliverableRecords = [];
|
|
20328
|
+
let unknownKinds = [];
|
|
19738
20329
|
const requiresFullRecover = !subscription.cursor.initialized || subscription.fileOffset === 0 || subscription.fileIdentity !== null && subscription.fileIdentity !== snapshot.identity || subscription.fileSize !== null && snapshot.size < subscription.fileOffset || subscription.fileSize !== null && snapshot.size === subscription.fileOffset && subscription.fileMtimeMs !== null && snapshot.mtimeMs !== subscription.fileMtimeMs;
|
|
19739
20330
|
if (requiresFullRecover) {
|
|
19740
20331
|
const previousCursor = subscription.cursor;
|
|
@@ -19743,7 +20334,8 @@ function readMirrorDeliverableRecords(subscription, snapshot) {
|
|
|
19743
20334
|
0,
|
|
19744
20335
|
snapshot.size,
|
|
19745
20336
|
"",
|
|
19746
|
-
null
|
|
20337
|
+
null,
|
|
20338
|
+
[]
|
|
19747
20339
|
);
|
|
19748
20340
|
const delta = reconcileDesktopMirrorCursor(subscription.cursor, fullDelta.records);
|
|
19749
20341
|
subscription.cursor = delta.nextCursor;
|
|
@@ -19751,6 +20343,8 @@ function readMirrorDeliverableRecords(subscription, snapshot) {
|
|
|
19751
20343
|
subscription.trailingText = "";
|
|
19752
20344
|
subscription.fileOffset = snapshot.size;
|
|
19753
20345
|
subscription.activeMirrorTurnId = fullDelta.nextTurnId;
|
|
20346
|
+
subscription.activeSpecialCallIds = new Set(fullDelta.nextSpecialCallIds);
|
|
20347
|
+
unknownKinds = fullDelta.unknownKinds;
|
|
19754
20348
|
} else if (snapshot.size > subscription.fileOffset || subscription.trailingText) {
|
|
19755
20349
|
const previousCursor = subscription.cursor;
|
|
19756
20350
|
const delta = readDesktopSessionMirrorRecordDeltaByFilePath(
|
|
@@ -19758,19 +20352,25 @@ function readMirrorDeliverableRecords(subscription, snapshot) {
|
|
|
19758
20352
|
subscription.fileOffset,
|
|
19759
20353
|
snapshot.size,
|
|
19760
20354
|
subscription.trailingText,
|
|
19761
|
-
subscription.activeMirrorTurnId
|
|
20355
|
+
subscription.activeMirrorTurnId,
|
|
20356
|
+
subscription.activeSpecialCallIds
|
|
19762
20357
|
);
|
|
19763
20358
|
deliverableRecords = filterDuplicateAssistantEvents(previousCursor, delta.records);
|
|
19764
20359
|
subscription.cursor = advanceDesktopMirrorCursor(subscription.cursor, delta.records);
|
|
19765
20360
|
subscription.trailingText = delta.trailingText;
|
|
19766
20361
|
subscription.fileOffset = delta.nextOffset;
|
|
19767
20362
|
subscription.activeMirrorTurnId = delta.nextTurnId;
|
|
20363
|
+
subscription.activeSpecialCallIds = new Set(delta.nextSpecialCallIds);
|
|
20364
|
+
unknownKinds = delta.unknownKinds;
|
|
19768
20365
|
}
|
|
19769
20366
|
subscription.fileSize = snapshot.size;
|
|
19770
20367
|
subscription.fileMtimeMs = snapshot.mtimeMs;
|
|
19771
20368
|
subscription.fileIdentity = snapshot.identity;
|
|
19772
20369
|
subscription.dirty = false;
|
|
19773
|
-
return
|
|
20370
|
+
return {
|
|
20371
|
+
records: deliverableRecords,
|
|
20372
|
+
unknownKinds
|
|
20373
|
+
};
|
|
19774
20374
|
}
|
|
19775
20375
|
|
|
19776
20376
|
// src/lib/bridge/mirror-delivery-plan.ts
|
|
@@ -19785,7 +20385,7 @@ function buildMirrorDeliveryPlan(subscription, deliverableRecords, options) {
|
|
|
19785
20385
|
if (options.blocked) {
|
|
19786
20386
|
return {
|
|
19787
20387
|
syncReason: "mirror reconcile active task",
|
|
19788
|
-
|
|
20388
|
+
finalizedTurns: timedOutTurn ? [timedOutTurn] : []
|
|
19789
20389
|
};
|
|
19790
20390
|
}
|
|
19791
20391
|
const finalizedTurns = timedOutTurn ? [timedOutTurn] : [];
|
|
@@ -19793,12 +20393,12 @@ function buildMirrorDeliveryPlan(subscription, deliverableRecords, options) {
|
|
|
19793
20393
|
if (finalizedTurns.length === 0) {
|
|
19794
20394
|
return {
|
|
19795
20395
|
syncReason: "mirror reconcile no finalized turns",
|
|
19796
|
-
|
|
20396
|
+
finalizedTurns: []
|
|
19797
20397
|
};
|
|
19798
20398
|
}
|
|
19799
20399
|
return {
|
|
19800
20400
|
syncReason: "mirror reconcile delivered turns",
|
|
19801
|
-
|
|
20401
|
+
finalizedTurns
|
|
19802
20402
|
};
|
|
19803
20403
|
}
|
|
19804
20404
|
|
|
@@ -20022,21 +20622,36 @@ function createMirrorRuntime(getState2, options, deps) {
|
|
|
20022
20622
|
deps.syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile unchanged snapshot");
|
|
20023
20623
|
return "processed";
|
|
20024
20624
|
}
|
|
20025
|
-
const
|
|
20625
|
+
const readResult = readMirrorDeliverableRecords(subscription, snapshot);
|
|
20626
|
+
const deliverableRecords = readResult.records;
|
|
20627
|
+
for (const kind of readResult.unknownKinds) {
|
|
20628
|
+
if (subscription.unknownMirrorKindsSeen.has(kind)) continue;
|
|
20629
|
+
subscription.unknownMirrorKindsSeen.add(kind);
|
|
20630
|
+
console.warn(
|
|
20631
|
+
`[bridge-manager] Unhandled desktop mirror event for thread ${subscription.threadId}: ${kind}`
|
|
20632
|
+
);
|
|
20633
|
+
}
|
|
20026
20634
|
if (deliverableRecords.length > 0) {
|
|
20027
20635
|
deps.observeSessionHealthRecords(subscription.sessionId, subscription.threadId, deliverableRecords);
|
|
20028
20636
|
}
|
|
20637
|
+
const blocked = getState2().activeTasks.has(subscription.sessionId) || deps.isMirrorSuppressed(subscription.sessionId);
|
|
20029
20638
|
const deliveryPlan = buildMirrorDeliveryPlan(subscription, deliverableRecords, {
|
|
20030
|
-
blocked
|
|
20639
|
+
blocked,
|
|
20031
20640
|
filterSuppressedRecords: deps.filterSuppressedMirrorRecords,
|
|
20032
20641
|
flushTimedOutTurn: (currentSubscription) => deps.flushTimedOutMirrorTurn(currentSubscription),
|
|
20033
20642
|
consumeBufferedTurns: (currentSubscription) => deps.consumeBufferedMirrorTurns(currentSubscription)
|
|
20034
20643
|
});
|
|
20035
|
-
if (deliveryPlan.
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
|
|
20039
|
-
|
|
20644
|
+
if (deliveryPlan.finalizedTurns.length > 0) {
|
|
20645
|
+
enqueuePendingMirrorDeliveries(subscription, deliveryPlan.finalizedTurns);
|
|
20646
|
+
}
|
|
20647
|
+
const turnsToAttempt = selectPendingMirrorDeliveries(subscription, blocked);
|
|
20648
|
+
if (turnsToAttempt.length > 0) {
|
|
20649
|
+
const deliveryResult = await deps.deliverMirrorTurns(subscription, turnsToAttempt);
|
|
20650
|
+
if (deliveryResult.deliveredCount > 0) {
|
|
20651
|
+
removePendingMirrorDeliveries(subscription, turnsToAttempt.slice(0, deliveryResult.deliveredCount));
|
|
20652
|
+
}
|
|
20653
|
+
if (deliveryResult.error) {
|
|
20654
|
+
const error = deliveryResult.error;
|
|
20040
20655
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
20041
20656
|
}
|
|
20042
20657
|
}
|
|
@@ -20210,11 +20825,13 @@ var HEALTH_RECENT_PROGRESS_MS = 10 * 60 * 1e3;
|
|
|
20210
20825
|
var HEALTH_SLOW_OBSERVED_MS = 30 * 60 * 1e3;
|
|
20211
20826
|
var HEALTH_PROGRESS_PERSIST_THROTTLE_MS = 15 * 1e3;
|
|
20212
20827
|
var HEALTH_PROCESS_PROBE_CACHE_MS = 30 * 1e3;
|
|
20828
|
+
var HEALTH_STREAM_UI_STALL_MS = 60 * 1e3;
|
|
20213
20829
|
var RUNNING_HEALTH_STATUSES = /* @__PURE__ */ new Set([
|
|
20214
20830
|
"running_active",
|
|
20215
20831
|
"waiting_tool",
|
|
20216
20832
|
"slow_observed",
|
|
20217
20833
|
"suspected_stall",
|
|
20834
|
+
"suspected_stream_ui_stall",
|
|
20218
20835
|
"suspected_detached"
|
|
20219
20836
|
]);
|
|
20220
20837
|
function parseIsoMs(value) {
|
|
@@ -20282,6 +20899,10 @@ function buildProgressReason(type, detail) {
|
|
|
20282
20899
|
return "\u6700\u8FD1\u6536\u5230\u4E86\u65B0\u7684\u684C\u9762\u4F1A\u8BDD\u6D88\u606F\u3002";
|
|
20283
20900
|
case "commentary":
|
|
20284
20901
|
return "\u6700\u8FD1\u6536\u5230\u4E86\u65B0\u7684\u6267\u884C\u8FDB\u5C55\u8BF4\u660E\u3002";
|
|
20902
|
+
case "reasoning":
|
|
20903
|
+
return "\u6700\u8FD1\u6536\u5230\u4E86\u65B0\u7684\u601D\u8003/\u72B6\u6001\u8BF4\u660E\u3002";
|
|
20904
|
+
case "plan_update":
|
|
20905
|
+
return "\u6700\u8FD1\u66F4\u65B0\u4E86\u4EFB\u52A1\u8BA1\u5212\u3002";
|
|
20285
20906
|
case "text":
|
|
20286
20907
|
return "\u6700\u8FD1\u6536\u5230\u4E86\u65B0\u7684\u6B63\u6587\u8F93\u51FA\u3002";
|
|
20287
20908
|
case "permission_wait":
|
|
@@ -20329,6 +20950,12 @@ function computeBaseDiagnosis(session, nowMs) {
|
|
|
20329
20950
|
const activeToolName = summarizeActiveTools(activeTools) || trimOrNull(session.active_tool_name);
|
|
20330
20951
|
const activeToolStartedAt = getActiveToolStartedAt(activeTools) || trimOrNull(session.active_tool_started_at);
|
|
20331
20952
|
const lastToolFinishedAt = trimOrNull(session.last_tool_finished_at);
|
|
20953
|
+
const lastStreamUiAttemptAt = trimOrNull(session.last_stream_ui_attempt_at);
|
|
20954
|
+
const lastStreamUiUpdateAt = trimOrNull(session.last_stream_ui_update_at);
|
|
20955
|
+
const streamUiFlushStartedAt = trimOrNull(session.stream_ui_flush_started_at);
|
|
20956
|
+
const lastStreamUiErrorAt = trimOrNull(session.last_stream_ui_error_at);
|
|
20957
|
+
const lastStreamUiError = trimOrNull(session.last_stream_ui_error);
|
|
20958
|
+
const streamUiConsecutiveFailures = typeof session.stream_ui_consecutive_failures === "number" && Number.isFinite(session.stream_ui_consecutive_failures) && session.stream_ui_consecutive_failures > 0 ? session.stream_ui_consecutive_failures : 0;
|
|
20332
20959
|
const sdkSessionId = trimOrNull(session.sdk_session_id);
|
|
20333
20960
|
const lastProgressMs = parseIsoMs(lastProgressAt || void 0);
|
|
20334
20961
|
const previousStatus = session.health_status || "idle";
|
|
@@ -20344,6 +20971,12 @@ function computeBaseDiagnosis(session, nowMs) {
|
|
|
20344
20971
|
activeToolName,
|
|
20345
20972
|
activeToolStartedAt,
|
|
20346
20973
|
lastToolFinishedAt,
|
|
20974
|
+
lastStreamUiAttemptAt,
|
|
20975
|
+
lastStreamUiUpdateAt,
|
|
20976
|
+
streamUiFlushStartedAt,
|
|
20977
|
+
lastStreamUiErrorAt,
|
|
20978
|
+
lastStreamUiError,
|
|
20979
|
+
streamUiConsecutiveFailures,
|
|
20347
20980
|
sdkSessionId
|
|
20348
20981
|
};
|
|
20349
20982
|
}
|
|
@@ -20375,6 +21008,12 @@ function computeBaseDiagnosis(session, nowMs) {
|
|
|
20375
21008
|
activeToolName,
|
|
20376
21009
|
activeToolStartedAt,
|
|
20377
21010
|
lastToolFinishedAt,
|
|
21011
|
+
lastStreamUiAttemptAt,
|
|
21012
|
+
lastStreamUiUpdateAt,
|
|
21013
|
+
streamUiFlushStartedAt,
|
|
21014
|
+
lastStreamUiErrorAt,
|
|
21015
|
+
lastStreamUiError,
|
|
21016
|
+
streamUiConsecutiveFailures,
|
|
20378
21017
|
sdkSessionId
|
|
20379
21018
|
};
|
|
20380
21019
|
}
|
|
@@ -20415,11 +21054,75 @@ function applyProcessProbeDiagnosis(diagnosis, processProbe) {
|
|
|
20415
21054
|
processProbe
|
|
20416
21055
|
};
|
|
20417
21056
|
}
|
|
21057
|
+
function applyStreamUiDiagnosis(diagnosis, nowMs) {
|
|
21058
|
+
if (!isRunningRuntimeStatus(diagnosis.runtimeStatus)) {
|
|
21059
|
+
return diagnosis;
|
|
21060
|
+
}
|
|
21061
|
+
const lastProgressMs = parseIsoMs(diagnosis.lastProgressAt || void 0);
|
|
21062
|
+
if (!lastProgressMs || nowMs - lastProgressMs > HEALTH_RECENT_PROGRESS_MS) {
|
|
21063
|
+
return diagnosis;
|
|
21064
|
+
}
|
|
21065
|
+
const lastStreamUiUpdateMs = parseIsoMs(diagnosis.lastStreamUiUpdateAt || void 0);
|
|
21066
|
+
const lastStreamUiAttemptMs = parseIsoMs(diagnosis.lastStreamUiAttemptAt || void 0);
|
|
21067
|
+
const streamUiFlushStartedMs = parseIsoMs(diagnosis.streamUiFlushStartedAt || void 0);
|
|
21068
|
+
const lastStreamUiErrorText = diagnosis.lastStreamUiError?.trim();
|
|
21069
|
+
if (streamUiFlushStartedMs && nowMs - streamUiFlushStartedMs >= HEALTH_STREAM_UI_STALL_MS) {
|
|
21070
|
+
const details = ["\u4EFB\u52A1\u4ECD\u5728\u7EE7\u7EED\uFF0C\u4F46\u6D41\u5F0F UI \u5237\u65B0\u8BF7\u6C42\u5DF2\u957F\u65F6\u95F4\u672A\u5B8C\u6210\uFF0C\u7591\u4F3C\u5361\u4F4F\u3002"];
|
|
21071
|
+
if (diagnosis.streamUiConsecutiveFailures > 0) {
|
|
21072
|
+
details.push(`\u6700\u8FD1\u8FDE\u7EED\u5931\u8D25 ${diagnosis.streamUiConsecutiveFailures} \u6B21\u3002`);
|
|
21073
|
+
}
|
|
21074
|
+
if (lastStreamUiErrorText) {
|
|
21075
|
+
details.push(`\u6700\u8FD1\u9519\u8BEF\uFF1A${lastStreamUiErrorText}`);
|
|
21076
|
+
}
|
|
21077
|
+
return {
|
|
21078
|
+
...diagnosis,
|
|
21079
|
+
healthStatus: "suspected_stream_ui_stall",
|
|
21080
|
+
healthReason: details.join(" ")
|
|
21081
|
+
};
|
|
21082
|
+
}
|
|
21083
|
+
if (lastStreamUiUpdateMs && lastProgressMs - lastStreamUiUpdateMs >= HEALTH_STREAM_UI_STALL_MS) {
|
|
21084
|
+
const details = ["\u4EFB\u52A1\u4ECD\u5728\u7EE7\u7EED\uFF0C\u4F46\u6D41\u5F0F UI \u5DF2\u957F\u65F6\u95F4\u6CA1\u6709\u8DDF\u4E0A\u6700\u65B0\u6267\u884C\u8FDB\u5C55\uFF0C\u7591\u4F3C\u505C\u66F4\u3002"];
|
|
21085
|
+
if (lastStreamUiErrorText) {
|
|
21086
|
+
details.push(`\u6700\u8FD1\u9519\u8BEF\uFF1A${lastStreamUiErrorText}`);
|
|
21087
|
+
}
|
|
21088
|
+
return {
|
|
21089
|
+
...diagnosis,
|
|
21090
|
+
healthStatus: "suspected_stream_ui_stall",
|
|
21091
|
+
healthReason: details.join(" ")
|
|
21092
|
+
};
|
|
21093
|
+
}
|
|
21094
|
+
if (!lastStreamUiUpdateMs && lastStreamUiAttemptMs && lastProgressMs - lastStreamUiAttemptMs >= HEALTH_STREAM_UI_STALL_MS) {
|
|
21095
|
+
const details = ["\u4EFB\u52A1\u4ECD\u5728\u7EE7\u7EED\uFF0C\u4F46\u6D41\u5F0F UI \u53EA\u6709\u53D1\u9001\u5C1D\u8BD5\u3001\u6CA1\u6709\u6210\u529F\u5237\u65B0\u8BB0\u5F55\uFF0C\u7591\u4F3C\u505C\u66F4\u3002"];
|
|
21096
|
+
if (lastStreamUiErrorText) {
|
|
21097
|
+
details.push(`\u6700\u8FD1\u9519\u8BEF\uFF1A${lastStreamUiErrorText}`);
|
|
21098
|
+
}
|
|
21099
|
+
return {
|
|
21100
|
+
...diagnosis,
|
|
21101
|
+
healthStatus: "suspected_stream_ui_stall",
|
|
21102
|
+
healthReason: details.join(" ")
|
|
21103
|
+
};
|
|
21104
|
+
}
|
|
21105
|
+
return diagnosis;
|
|
21106
|
+
}
|
|
20418
21107
|
|
|
20419
21108
|
// src/lib/bridge/session-health-runtime.ts
|
|
20420
21109
|
function createSessionHealthRuntime(deps) {
|
|
20421
21110
|
const lastProgressPersistAt = /* @__PURE__ */ new Map();
|
|
20422
21111
|
const processProbeCache = /* @__PURE__ */ new Map();
|
|
21112
|
+
function summarizePlanUpdate(tasks) {
|
|
21113
|
+
if (!Array.isArray(tasks) || tasks.length === 0) {
|
|
21114
|
+
return "\u68C0\u6D4B\u5230\u684C\u9762\u7EBF\u7A0B\u66F4\u65B0\u4E86\u4EFB\u52A1\u8BA1\u5212\u3002";
|
|
21115
|
+
}
|
|
21116
|
+
let inProgress = 0;
|
|
21117
|
+
let pending = 0;
|
|
21118
|
+
let completed = 0;
|
|
21119
|
+
for (const task of tasks) {
|
|
21120
|
+
if (task?.status === "completed") completed += 1;
|
|
21121
|
+
else if (task?.status === "in_progress") inProgress += 1;
|
|
21122
|
+
else pending += 1;
|
|
21123
|
+
}
|
|
21124
|
+
return `\u68C0\u6D4B\u5230\u684C\u9762\u7EBF\u7A0B\u66F4\u65B0\u4E86\u4EFB\u52A1\u8BA1\u5212\uFF08\u6267\u884C\u4E2D ${inProgress} \u9879\uFF0C\u7B49\u5F85\u4E2D ${pending} \u9879\uFF0C\u5DF2\u5B8C\u6210 ${completed} \u9879\uFF09\u3002`;
|
|
21125
|
+
}
|
|
20423
21126
|
function updateSessionHealth(sessionId, updates, options) {
|
|
20424
21127
|
const store = deps.getStore();
|
|
20425
21128
|
const session = store.getSession(sessionId);
|
|
@@ -20462,7 +21165,13 @@ function createSessionHealthRuntime(deps) {
|
|
|
20462
21165
|
active_tools_json: void 0,
|
|
20463
21166
|
active_tool_name: void 0,
|
|
20464
21167
|
active_tool_started_at: void 0,
|
|
20465
|
-
last_tool_finished_at: void 0
|
|
21168
|
+
last_tool_finished_at: void 0,
|
|
21169
|
+
last_stream_ui_attempt_at: void 0,
|
|
21170
|
+
last_stream_ui_update_at: void 0,
|
|
21171
|
+
stream_ui_flush_started_at: void 0,
|
|
21172
|
+
last_stream_ui_error_at: void 0,
|
|
21173
|
+
last_stream_ui_error: void 0,
|
|
21174
|
+
stream_ui_consecutive_failures: void 0
|
|
20466
21175
|
});
|
|
20467
21176
|
}
|
|
20468
21177
|
function recordInteractiveProgress(sessionId, type, detail) {
|
|
@@ -20530,11 +21239,29 @@ function createSessionHealthRuntime(deps) {
|
|
|
20530
21239
|
active_tools_json: void 0,
|
|
20531
21240
|
active_tool_name: void 0,
|
|
20532
21241
|
active_tool_started_at: void 0,
|
|
21242
|
+
stream_ui_flush_started_at: void 0,
|
|
20533
21243
|
last_health_check_at: nowIso4
|
|
20534
21244
|
}, { force: true });
|
|
20535
21245
|
lastProgressPersistAt.set(sessionId, Date.now());
|
|
20536
21246
|
processProbeCache.delete(sessionId);
|
|
20537
21247
|
}
|
|
21248
|
+
function toIso(value) {
|
|
21249
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
|
|
21250
|
+
return new Date(value).toISOString();
|
|
21251
|
+
}
|
|
21252
|
+
function recordStructuredStreamUi(sessionId, snapshot) {
|
|
21253
|
+
const updates = {
|
|
21254
|
+
stream_ui_flush_started_at: snapshot.active && snapshot.flushInFlight ? toIso(snapshot.flushInFlightSince ?? snapshot.lastAttemptAt) : void 0
|
|
21255
|
+
};
|
|
21256
|
+
if (snapshot.active) {
|
|
21257
|
+
updates.last_stream_ui_attempt_at = toIso(snapshot.lastAttemptAt);
|
|
21258
|
+
updates.last_stream_ui_update_at = toIso(snapshot.lastUpdateAt);
|
|
21259
|
+
updates.last_stream_ui_error_at = toIso(snapshot.lastErrorAt);
|
|
21260
|
+
updates.last_stream_ui_error = snapshot.lastError?.trim() || void 0;
|
|
21261
|
+
updates.stream_ui_consecutive_failures = snapshot.consecutiveFailures && snapshot.consecutiveFailures > 0 ? snapshot.consecutiveFailures : void 0;
|
|
21262
|
+
}
|
|
21263
|
+
updateSessionHealth(sessionId, updates);
|
|
21264
|
+
}
|
|
20538
21265
|
function observeDesktopMirrorRecords(sessionId, _threadId, records) {
|
|
20539
21266
|
for (const record of records) {
|
|
20540
21267
|
if (record.type === "task_started") {
|
|
@@ -20545,6 +21272,10 @@ function createSessionHealthRuntime(deps) {
|
|
|
20545
21272
|
recordInteractiveEnd(sessionId, "completed", "\u68C0\u6D4B\u5230\u684C\u9762\u7EBF\u7A0B\u5DF2\u5B8C\u6210\u5F53\u524D\u4EFB\u52A1\u3002");
|
|
20546
21273
|
continue;
|
|
20547
21274
|
}
|
|
21275
|
+
if (record.type === "task_aborted") {
|
|
21276
|
+
recordInteractiveEnd(sessionId, "aborted", "\u68C0\u6D4B\u5230\u684C\u9762\u7EBF\u7A0B\u5DF2\u505C\u6B62\u5F53\u524D\u4EFB\u52A1\u3002");
|
|
21277
|
+
continue;
|
|
21278
|
+
}
|
|
20548
21279
|
if (record.type === "tool_started") {
|
|
20549
21280
|
recordToolState(sessionId, record.toolId || record.signature, record.toolName || "tool", "running");
|
|
20550
21281
|
continue;
|
|
@@ -20558,6 +21289,22 @@ function createSessionHealthRuntime(deps) {
|
|
|
20558
21289
|
);
|
|
20559
21290
|
continue;
|
|
20560
21291
|
}
|
|
21292
|
+
if (record.type === "reasoning") {
|
|
21293
|
+
recordInteractiveProgress(
|
|
21294
|
+
sessionId,
|
|
21295
|
+
"reasoning",
|
|
21296
|
+
"\u68C0\u6D4B\u5230\u684C\u9762\u7EBF\u7A0B\u65B0\u7684\u601D\u8003/\u72B6\u6001\u8BF4\u660E\u3002"
|
|
21297
|
+
);
|
|
21298
|
+
continue;
|
|
21299
|
+
}
|
|
21300
|
+
if (record.type === "plan_update") {
|
|
21301
|
+
recordInteractiveProgress(
|
|
21302
|
+
sessionId,
|
|
21303
|
+
"plan_update",
|
|
21304
|
+
summarizePlanUpdate(record.tasks)
|
|
21305
|
+
);
|
|
21306
|
+
continue;
|
|
21307
|
+
}
|
|
20561
21308
|
if (record.type === "message") {
|
|
20562
21309
|
recordInteractiveProgress(
|
|
20563
21310
|
sessionId,
|
|
@@ -20572,7 +21319,10 @@ function createSessionHealthRuntime(deps) {
|
|
|
20572
21319
|
const store = deps.getStore();
|
|
20573
21320
|
for (const session of store.listSessions()) {
|
|
20574
21321
|
if (!shouldTrackSession(session)) continue;
|
|
20575
|
-
const diagnosis =
|
|
21322
|
+
const diagnosis = applyStreamUiDiagnosis(
|
|
21323
|
+
{ ...computeBaseDiagnosis(session, nowMs), processProbe: null },
|
|
21324
|
+
nowMs
|
|
21325
|
+
);
|
|
20576
21326
|
updateSessionHealth(session.id, {
|
|
20577
21327
|
health_status: diagnosis.healthStatus,
|
|
20578
21328
|
health_reason: diagnosis.healthReason
|
|
@@ -20600,7 +21350,10 @@ function createSessionHealthRuntime(deps) {
|
|
|
20600
21350
|
if (!session) return null;
|
|
20601
21351
|
const base = computeBaseDiagnosis(session, Date.now());
|
|
20602
21352
|
const processProbe = await loadProcessProbe(session);
|
|
20603
|
-
const diagnosis =
|
|
21353
|
+
const diagnosis = applyStreamUiDiagnosis(
|
|
21354
|
+
applyProcessProbeDiagnosis(base, processProbe),
|
|
21355
|
+
Date.now()
|
|
21356
|
+
);
|
|
20604
21357
|
updateSessionHealth(sessionId, {
|
|
20605
21358
|
health_status: diagnosis.healthStatus,
|
|
20606
21359
|
health_reason: diagnosis.healthReason,
|
|
@@ -20618,6 +21371,7 @@ function createSessionHealthRuntime(deps) {
|
|
|
20618
21371
|
recordInteractiveStart,
|
|
20619
21372
|
recordInteractiveProgress,
|
|
20620
21373
|
recordToolState,
|
|
21374
|
+
recordStructuredStreamUi,
|
|
20621
21375
|
recordInteractiveEnd,
|
|
20622
21376
|
observeDesktopMirrorRecords,
|
|
20623
21377
|
reconcileSessionHealth,
|
|
@@ -20871,7 +21625,8 @@ function pushMirrorStreamingStatus(subscription, turnState, options = {}) {
|
|
|
20871
21625
|
}
|
|
20872
21626
|
const statusText = formatInteractiveRuntimeStatus(
|
|
20873
21627
|
Math.max(0, nowMs - startedAtMs),
|
|
20874
|
-
options.silentMs
|
|
21628
|
+
options.silentMs,
|
|
21629
|
+
turnState.statusNote
|
|
20875
21630
|
);
|
|
20876
21631
|
if (turnState.lastStatusText === statusText) return;
|
|
20877
21632
|
pushStreamFeedbackStatus(
|
|
@@ -20920,6 +21675,20 @@ function updateMirrorToolProgress(subscription, turnState) {
|
|
|
20920
21675
|
);
|
|
20921
21676
|
pushMirrorStreamingStatus(subscription, turnState);
|
|
20922
21677
|
}
|
|
21678
|
+
function updateMirrorTaskProgress(subscription, turnState) {
|
|
21679
|
+
const adapter = getMirrorStreamingAdapter(subscription);
|
|
21680
|
+
if (!adapter) return;
|
|
21681
|
+
pushStreamFeedbackTasks(
|
|
21682
|
+
createMirrorStreamFeedbackTarget(subscription, turnState, adapter),
|
|
21683
|
+
turnState.taskItems
|
|
21684
|
+
);
|
|
21685
|
+
pushMirrorStreamingStatus(subscription, turnState);
|
|
21686
|
+
}
|
|
21687
|
+
function updateMirrorStatusProgress(subscription, turnState) {
|
|
21688
|
+
const adapter = getMirrorStreamingAdapter(subscription);
|
|
21689
|
+
if (!adapter) return;
|
|
21690
|
+
pushMirrorStreamingStatus(subscription, turnState);
|
|
21691
|
+
}
|
|
20923
21692
|
function stopMirrorStreaming(subscription, status = "interrupted") {
|
|
20924
21693
|
const adapter = getMirrorStreamingAdapter(subscription);
|
|
20925
21694
|
const pendingTurn = subscription.pendingTurn;
|
|
@@ -20980,12 +21749,21 @@ async function deliverMirrorTurn(subscription, turn) {
|
|
|
20980
21749
|
subscription.lastDeliveredAt = turn.timestamp || nowIso3();
|
|
20981
21750
|
}
|
|
20982
21751
|
async function deliverMirrorTurns(subscription, turns) {
|
|
20983
|
-
|
|
20984
|
-
|
|
21752
|
+
let deliveredCount = 0;
|
|
21753
|
+
for (const turn of turns.slice(0, MIRROR_EVENT_BATCH_LIMIT)) {
|
|
21754
|
+
try {
|
|
21755
|
+
await deliverMirrorTurn(subscription, turn);
|
|
21756
|
+
deliveredCount += 1;
|
|
21757
|
+
} catch (error) {
|
|
21758
|
+
return { deliveredCount, error };
|
|
21759
|
+
}
|
|
20985
21760
|
}
|
|
21761
|
+
return { deliveredCount };
|
|
20986
21762
|
}
|
|
20987
21763
|
var MIRROR_TURN_HOOKS = {
|
|
20988
21764
|
onStreamText: updateMirrorStreaming,
|
|
21765
|
+
onStatusProgress: updateMirrorStatusProgress,
|
|
21766
|
+
onTaskProgress: updateMirrorTaskProgress,
|
|
20989
21767
|
onToolProgress: updateMirrorToolProgress
|
|
20990
21768
|
};
|
|
20991
21769
|
function consumeMirrorRecords2(subscription, records) {
|
|
@@ -21272,6 +22050,9 @@ async function handleMessage(adapter, msg) {
|
|
|
21272
22050
|
recordInteractiveHealthTool: (sessionId, toolId, toolName, status) => {
|
|
21273
22051
|
SESSION_HEALTH_RUNTIME.recordToolState(sessionId, toolId, toolName, status);
|
|
21274
22052
|
},
|
|
22053
|
+
recordInteractiveStreamUiSnapshot: (sessionId, snapshot) => {
|
|
22054
|
+
SESSION_HEALTH_RUNTIME.recordStructuredStreamUi(sessionId, snapshot);
|
|
22055
|
+
},
|
|
21275
22056
|
recordInteractiveHealthEnd: (sessionId, outcome, detail) => SESSION_HEALTH_RUNTIME.recordInteractiveEnd(sessionId, outcome, detail),
|
|
21276
22057
|
beginMirrorSuppression: beginMirrorSuppression2,
|
|
21277
22058
|
abortMirrorSuppression: abortMirrorSuppression2,
|