@keyou007/agent-bot 0.1.24-alpha.0 → 0.1.24-alpha.2
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/CHANGELOG.md +17 -0
- package/README.md +24 -4
- package/README.zh.md +24 -4
- package/dist/cli/SelfUpdateRunner.js +2 -0
- package/dist/cli/SelfUpdateRunner.js.map +1 -1
- package/dist/codex/CodexEventMapper.js +90 -2
- package/dist/codex/CodexEventMapper.js.map +1 -1
- package/dist/codex/CodexRuntime.js +160 -12
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +14 -1
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +42 -11
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +70 -4
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +2 -1
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +16 -1
- package/dist/local-files/TurnPreviewPage.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +37 -7
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +58 -38
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +2 -0
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +7 -0
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +12 -0
- package/src/cli/SelfUpdateRunner.ts +2 -0
- package/src/codex/CodexEventMapper.ts +97 -3
- package/src/codex/CodexRuntime.ts +163 -13
- package/src/commands/CommandRouter.ts +13 -2
- package/src/commands/commandTypes.ts +2 -2
- package/src/feishu/CardRenderer.ts +48 -12
- package/src/feishu/FeishuMarkdown.ts +69 -3
- package/src/feishu/FeishuTurnPresenter.ts +1 -0
- package/src/index.ts +4 -0
- package/src/local-files/TurnPreviewPage.ts +16 -1
- package/src/presentation/TurnStateReducer.ts +49 -9
- package/src/presentation/turnViewTypes.ts +4 -0
- package/src/proxy/ProxySessionController.ts +61 -40
- package/src/runtime/types.ts +13 -1
- package/src/supervision/SafeRestartNotifier.ts +2 -0
- package/src/supervision/SafeRestartScheduler.ts +7 -0
|
@@ -2,7 +2,7 @@ import { statSync } from "node:fs";
|
|
|
2
2
|
import { appendGeneratedImageMarkdown } from "../utils/generatedImageMarkdown.js";
|
|
3
3
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
4
4
|
import { AppServerRequestError } from "./AppServerConnection.js";
|
|
5
|
-
import { mapCodexNotification } from "./CodexEventMapper.js";
|
|
5
|
+
import { formatCodexError, mapCodexNotification } from "./CodexEventMapper.js";
|
|
6
6
|
import { CodexLocalActivityDetector } from "./CodexLocalActivityDetector.js";
|
|
7
7
|
import { detectProjectlessWorkspace } from "./ProjectlessWorkspace.js";
|
|
8
8
|
import { threadWriterLockPath } from "./ThreadWriterProcess.js";
|
|
@@ -23,6 +23,7 @@ const FORK_SOURCE_TURN_PAGE_SIZE = 20;
|
|
|
23
23
|
const FORK_SOURCE_REQUEST_TIMEOUT_MS = 15_000;
|
|
24
24
|
const USER_RESUMABLE_THREAD_SOURCE_KINDS = ["cli", "vscode", "exec", "appServer"];
|
|
25
25
|
const BUILT_IN_CODEX_PROVIDER_ID = "openai";
|
|
26
|
+
const BUILT_IN_TRAEX_PROVIDER_ID = "trae";
|
|
26
27
|
// A timed-out fork keeps running in App Server and can create an orphan thread.
|
|
27
28
|
// Wait for its response; connection closure still rejects the request.
|
|
28
29
|
const FORK_REQUEST_TIMEOUT_MS = 0;
|
|
@@ -33,6 +34,7 @@ export class CodexRuntime {
|
|
|
33
34
|
sessions = new Map();
|
|
34
35
|
listeners = new Set();
|
|
35
36
|
approvals = new Map();
|
|
37
|
+
modeChanges = new Map();
|
|
36
38
|
attachedClient;
|
|
37
39
|
unsubscribe;
|
|
38
40
|
unsubscribeDisconnect;
|
|
@@ -648,6 +650,37 @@ export class CodexRuntime {
|
|
|
648
650
|
}
|
|
649
651
|
}
|
|
650
652
|
async respondToApproval(sessionId, requestId, decision) {
|
|
653
|
+
const modeChange = this.modeChanges.get(requestId);
|
|
654
|
+
if (modeChange) {
|
|
655
|
+
if (modeChange.sessionId !== sessionId
|
|
656
|
+
|| this.sessions.get(sessionId)?.activeTurnId !== modeChange.turnId) {
|
|
657
|
+
throw new Error("Approval request is no longer pending.");
|
|
658
|
+
}
|
|
659
|
+
if (decision !== "accept" && decision !== "decline" && decision !== "cancel") {
|
|
660
|
+
throw new Error("Invalid mode change decision.");
|
|
661
|
+
}
|
|
662
|
+
if (modeChange.responding)
|
|
663
|
+
throw new Error("Approval response is already in progress.");
|
|
664
|
+
modeChange.responding = true;
|
|
665
|
+
try {
|
|
666
|
+
const client = await this.client();
|
|
667
|
+
if (this.modeChanges.get(requestId) !== modeChange)
|
|
668
|
+
throw new Error("Approval request is no longer pending.");
|
|
669
|
+
await client.request("mode/change/respond", {
|
|
670
|
+
threadId: this.requireSession(sessionId).remoteSessionId,
|
|
671
|
+
requestId: modeChange.requestId,
|
|
672
|
+
decision: decision === "accept" ? "approved" : decision === "decline" ? "rejected" : "cancelled",
|
|
673
|
+
}, CONTROL_REQUEST_TIMEOUT_MS);
|
|
674
|
+
if (this.modeChanges.get(requestId) === modeChange) {
|
|
675
|
+
this.modeChanges.delete(requestId);
|
|
676
|
+
this.emit({ type: "approval_resolved", sessionId, turnId: modeChange.turnId, requestId, decision });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
finally {
|
|
680
|
+
modeChange.responding = false;
|
|
681
|
+
}
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
651
684
|
const pending = this.approvals.get(requestId);
|
|
652
685
|
if (!pending || pending.sessionId !== sessionId)
|
|
653
686
|
throw new Error("Approval request is no longer pending.");
|
|
@@ -656,10 +689,15 @@ export class CodexRuntime {
|
|
|
656
689
|
this.emit({ type: "approval_resolved", sessionId, turnId: pending.turnId, requestId, decision });
|
|
657
690
|
}
|
|
658
691
|
async listModels(modelProvider, fallbackModel) {
|
|
692
|
+
return (await this.listModelsWithStatus(modelProvider, fallbackModel)).models;
|
|
693
|
+
}
|
|
694
|
+
async listModelsWithStatus(modelProvider, fallbackModel) {
|
|
659
695
|
const requestedProvider = modelProvider?.trim();
|
|
696
|
+
const agentFamily = this.provider.getAgentFamily?.();
|
|
660
697
|
if (!requestedProvider
|
|
661
|
-
|| (requestedProvider === BUILT_IN_CODEX_PROVIDER_ID &&
|
|
662
|
-
|
|
698
|
+
|| (requestedProvider === BUILT_IN_CODEX_PROVIDER_ID && agentFamily !== "traex")
|
|
699
|
+
|| (requestedProvider === BUILT_IN_TRAEX_PROVIDER_ID && agentFamily === "traex")) {
|
|
700
|
+
return { models: await this.listCatalogModels() };
|
|
663
701
|
}
|
|
664
702
|
const config = await this.readConfig();
|
|
665
703
|
const configuredProviders = isRecord(config.model_providers) ? config.model_providers : {};
|
|
@@ -675,20 +713,24 @@ export class CodexRuntime {
|
|
|
675
713
|
? stringValue(config.model)?.trim()
|
|
676
714
|
: undefined;
|
|
677
715
|
try {
|
|
678
|
-
|
|
716
|
+
const models = await fetchProviderModels({
|
|
679
717
|
providerId: requestedProvider,
|
|
680
718
|
config: (isRecord(providerConfig) ? providerConfig : {}),
|
|
681
719
|
catalog,
|
|
682
720
|
configuredDefaultModel,
|
|
683
721
|
environmentValue: (name) => this.provider.getEnvironmentVariable?.(name) ?? process.env[name],
|
|
684
722
|
});
|
|
723
|
+
return { models };
|
|
685
724
|
}
|
|
686
725
|
catch (error) {
|
|
687
726
|
const fallback = fallbackProviderModel(catalog, fallbackModel, configuredDefaultModel);
|
|
688
727
|
if (!fallback)
|
|
689
728
|
throw error;
|
|
690
729
|
this.logger.warn({ error, modelProvider: requestedProvider, fallbackModel: fallback.id }, "Provider model discovery failed; using the current or configured model as a fallback.");
|
|
691
|
-
return
|
|
730
|
+
return {
|
|
731
|
+
models: [fallback],
|
|
732
|
+
warning: `${error instanceof Error ? error.message : "Provider 模型列表读取失败。"} 暂时仅显示当前或已配置模型,服务可用性未确认。`,
|
|
733
|
+
};
|
|
692
734
|
}
|
|
693
735
|
}
|
|
694
736
|
async listCatalogModels() {
|
|
@@ -771,6 +813,7 @@ export class CodexRuntime {
|
|
|
771
813
|
for (const pending of this.approvals.values())
|
|
772
814
|
pending.resolve({ decision: "cancel" });
|
|
773
815
|
this.approvals.clear();
|
|
816
|
+
this.modeChanges.clear();
|
|
774
817
|
this.sessionSyncs.clear();
|
|
775
818
|
this.sessions.clear();
|
|
776
819
|
this.provider.close();
|
|
@@ -858,13 +901,54 @@ export class CodexRuntime {
|
|
|
858
901
|
return;
|
|
859
902
|
}
|
|
860
903
|
const sessionId = session.localSessionId;
|
|
861
|
-
if (mapped.kind === "
|
|
904
|
+
if (mapped.kind === "mode_change_requested") {
|
|
905
|
+
const requestId = `mode:${mapped.threadId}:${mapped.requestId}`;
|
|
906
|
+
if (session.modeChangeRequestIds.has(requestId))
|
|
907
|
+
return;
|
|
908
|
+
session.modeChangeRequestIds.add(requestId);
|
|
909
|
+
this.modeChanges.set(requestId, {
|
|
910
|
+
sessionId, turnId: mapped.turnId, requestId: mapped.requestId,
|
|
911
|
+
targetMode: mapped.targetMode, responding: false,
|
|
912
|
+
});
|
|
913
|
+
this.emit({
|
|
914
|
+
type: "approval_requested", sessionId, turnId: mapped.turnId,
|
|
915
|
+
request: {
|
|
916
|
+
id: requestId,
|
|
917
|
+
kind: "mode_change",
|
|
918
|
+
title: mapped.targetMode === "default" ? "确认方案并开始执行" : "确认进入规划模式",
|
|
919
|
+
reason: [mapped.reason, ...(mapped.allowedPrompts ?? []).map((entry) => `- ${entry.tool}: ${entry.prompt}`)]
|
|
920
|
+
.filter(Boolean).join("\n"),
|
|
921
|
+
options: [
|
|
922
|
+
{ id: "accept", label: mapped.targetMode === "default" ? "Approve Plan" : "Enter Plan Mode" },
|
|
923
|
+
{ id: "decline", label: "Reject" },
|
|
924
|
+
{ id: "cancel", label: "Cancel Request" },
|
|
925
|
+
],
|
|
926
|
+
},
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
else if (mapped.kind === "mode_change_resolved") {
|
|
930
|
+
const requestId = `mode:${mapped.threadId}:${mapped.requestId}`;
|
|
931
|
+
const pending = this.modeChanges.get(requestId);
|
|
932
|
+
if (!pending || pending.sessionId !== sessionId || pending.turnId !== mapped.turnId
|
|
933
|
+
|| pending.targetMode !== mapped.targetMode)
|
|
934
|
+
return;
|
|
935
|
+
this.modeChanges.delete(requestId);
|
|
936
|
+
this.emit({
|
|
937
|
+
type: "approval_resolved", sessionId, turnId: mapped.turnId, requestId,
|
|
938
|
+
decision: mapped.decision === "approved" ? "accept" : mapped.decision === "rejected" ? "decline" : "cancel",
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
else if (mapped.kind === "token_usage") {
|
|
862
942
|
this.emit({
|
|
863
943
|
type: "token_usage_updated",
|
|
864
944
|
sessionId,
|
|
865
945
|
turnId: mapped.turnId,
|
|
866
946
|
lastTokens: mapped.lastTokens,
|
|
867
947
|
cumulativeTokens: mapped.cumulativeTokens,
|
|
948
|
+
lastTotalTokens: mapped.lastTotalTokens,
|
|
949
|
+
cumulativeTotalTokens: mapped.cumulativeTotalTokens,
|
|
950
|
+
lastCachedTokens: mapped.lastCachedTokens,
|
|
951
|
+
cumulativeCachedTokens: mapped.cumulativeCachedTokens,
|
|
868
952
|
contextTokens: mapped.contextTokens,
|
|
869
953
|
});
|
|
870
954
|
}
|
|
@@ -882,9 +966,19 @@ export class CodexRuntime {
|
|
|
882
966
|
}
|
|
883
967
|
else if (mapped.kind === "agent_message_phase") {
|
|
884
968
|
session.messagePhases.set(mapped.itemId, mapped.phase);
|
|
969
|
+
if (mapped.phase === "final_answer")
|
|
970
|
+
this.promoteUnphasedMessage(session, mapped.turnId, mapped.itemId);
|
|
971
|
+
else if (session.unphasedFinalMessageId === mapped.itemId)
|
|
972
|
+
session.unphasedFinalMessageId = undefined;
|
|
885
973
|
}
|
|
886
974
|
else if (mapped.kind === "agent_delta") {
|
|
887
|
-
|
|
975
|
+
const phase = session.messagePhases.get(mapped.itemId);
|
|
976
|
+
if (phase !== "final_answer") {
|
|
977
|
+
// Providers may omit phase. Keep their text in sequence until a tool or completion disambiguates it.
|
|
978
|
+
if (phase === undefined) {
|
|
979
|
+
session.unphasedMessages.set(mapped.itemId, (session.unphasedMessages.get(mapped.itemId) ?? "") + mapped.text);
|
|
980
|
+
session.unphasedFinalMessageId = mapped.itemId;
|
|
981
|
+
}
|
|
888
982
|
this.emit({
|
|
889
983
|
type: "progress",
|
|
890
984
|
sessionId,
|
|
@@ -899,6 +993,19 @@ export class CodexRuntime {
|
|
|
899
993
|
this.emit({ type: "agent_text_delta", sessionId, turnId: mapped.turnId, text: mapped.text });
|
|
900
994
|
}
|
|
901
995
|
}
|
|
996
|
+
else if (mapped.kind === "runtime_error") {
|
|
997
|
+
this.logger.warn({ sessionId, turnId: mapped.turnId, willRetry: mapped.willRetry, error: mapped.message }, "App Server reported a turn error.");
|
|
998
|
+
// Only turn/completed terminates the turn; native retries keep the same progress card.
|
|
999
|
+
this.emit({
|
|
1000
|
+
type: "progress",
|
|
1001
|
+
sessionId,
|
|
1002
|
+
turnId: mapped.turnId,
|
|
1003
|
+
activityId: `commentary:runtime-error:${mapped.turnId}`,
|
|
1004
|
+
text: `${mapped.willRetry ? "运行请求出错,Agent 正在重试" : "运行请求失败,等待 Agent 返回最终状态"}:${mapped.message}`,
|
|
1005
|
+
append: false,
|
|
1006
|
+
severity: "warning",
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
902
1009
|
else if (mapped.kind === "progress") {
|
|
903
1010
|
this.emit({
|
|
904
1011
|
type: "progress",
|
|
@@ -913,6 +1020,8 @@ export class CodexRuntime {
|
|
|
913
1020
|
this.emit({ type: "plan_updated", sessionId, turnId: mapped.turnId, steps: mapped.steps });
|
|
914
1021
|
}
|
|
915
1022
|
else if (mapped.kind === "tool") {
|
|
1023
|
+
if (mapped.phase === "started")
|
|
1024
|
+
session.unphasedFinalMessageId = undefined;
|
|
916
1025
|
if (mapped.phase === "updated"
|
|
917
1026
|
&& mapped.tool.kind === "image_generation"
|
|
918
1027
|
&& mapped.tool.status === "completed"
|
|
@@ -936,6 +1045,11 @@ export class CodexRuntime {
|
|
|
936
1045
|
});
|
|
937
1046
|
}
|
|
938
1047
|
else if (mapped.kind === "terminal") {
|
|
1048
|
+
if (mapped.status === "completed" && !session.finalText && session.unphasedFinalMessageId) {
|
|
1049
|
+
this.promoteUnphasedMessage(session, mapped.turnId, session.unphasedFinalMessageId);
|
|
1050
|
+
}
|
|
1051
|
+
session.unphasedMessages.clear();
|
|
1052
|
+
session.unphasedFinalMessageId = undefined;
|
|
939
1053
|
session.activeTurnId = undefined;
|
|
940
1054
|
session.activeTurnStartedAt = undefined;
|
|
941
1055
|
session.terminalTurnIds.add(mapped.turnId);
|
|
@@ -992,6 +1106,19 @@ export class CodexRuntime {
|
|
|
992
1106
|
});
|
|
993
1107
|
return response;
|
|
994
1108
|
}
|
|
1109
|
+
promoteUnphasedMessage(session, turnId, itemId) {
|
|
1110
|
+
const text = session.unphasedMessages.get(itemId);
|
|
1111
|
+
if (text === undefined)
|
|
1112
|
+
return;
|
|
1113
|
+
session.unphasedMessages.delete(itemId);
|
|
1114
|
+
if (session.unphasedFinalMessageId === itemId)
|
|
1115
|
+
session.unphasedFinalMessageId = undefined;
|
|
1116
|
+
session.finalText += text;
|
|
1117
|
+
this.emit({
|
|
1118
|
+
type: "agent_text_delta", sessionId: session.localSessionId, turnId, text,
|
|
1119
|
+
replacesActivityId: `commentary:${itemId}`,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
995
1122
|
async resolveReasoningEffort(input, response) {
|
|
996
1123
|
if (input.reasoningEffort)
|
|
997
1124
|
return input.reasoningEffort;
|
|
@@ -1081,6 +1208,9 @@ export class CodexRuntime {
|
|
|
1081
1208
|
}
|
|
1082
1209
|
session.activeTurnId = turnId;
|
|
1083
1210
|
session.activeTurnStartedAt = startedAt;
|
|
1211
|
+
session.modeChangeRequestIds.clear();
|
|
1212
|
+
session.unphasedMessages.clear();
|
|
1213
|
+
session.unphasedFinalMessageId = undefined;
|
|
1084
1214
|
session.finalText = "";
|
|
1085
1215
|
session.generatedImagePaths = [];
|
|
1086
1216
|
session.messagePhases.clear();
|
|
@@ -1098,6 +1228,14 @@ export class CodexRuntime {
|
|
|
1098
1228
|
finishSnapshotTurn(session, turn) {
|
|
1099
1229
|
if (session.activeTurnId !== turn.id)
|
|
1100
1230
|
return;
|
|
1231
|
+
if (turn.status === "completed") {
|
|
1232
|
+
for (const item of finalResponseMessages(turn)) {
|
|
1233
|
+
if (item.id)
|
|
1234
|
+
this.promoteUnphasedMessage(session, turn.id, item.id);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
session.unphasedMessages.clear();
|
|
1238
|
+
session.unphasedFinalMessageId = undefined;
|
|
1101
1239
|
session.activeTurnId = undefined;
|
|
1102
1240
|
session.activeTurnStartedAt = undefined;
|
|
1103
1241
|
session.terminalTurnIds.add(turn.id);
|
|
@@ -1111,7 +1249,7 @@ export class CodexRuntime {
|
|
|
1111
1249
|
type: "turn_failed",
|
|
1112
1250
|
sessionId: session.localSessionId,
|
|
1113
1251
|
turnId: turn.id,
|
|
1114
|
-
message: turn.error
|
|
1252
|
+
message: formatCodexError(turn.error) ?? "App Server turn failed.",
|
|
1115
1253
|
});
|
|
1116
1254
|
return;
|
|
1117
1255
|
}
|
|
@@ -1152,9 +1290,11 @@ export class CodexRuntime {
|
|
|
1152
1290
|
&& input.lastTurnStatus !== "running"
|
|
1153
1291
|
? [input.lastTurnId]
|
|
1154
1292
|
: []),
|
|
1293
|
+
modeChangeRequestIds: new Set(),
|
|
1155
1294
|
finalText: "",
|
|
1156
1295
|
generatedImagePaths: [],
|
|
1157
1296
|
messagePhases: new Map(),
|
|
1297
|
+
unphasedMessages: new Map(),
|
|
1158
1298
|
needsResume: false,
|
|
1159
1299
|
canReplaceEmptyThread: !("remoteSessionId" in input),
|
|
1160
1300
|
rolloutPath: stringValue(response.thread.path)?.trim() || undefined,
|
|
@@ -1187,6 +1327,12 @@ export class CodexRuntime {
|
|
|
1187
1327
|
return session;
|
|
1188
1328
|
}
|
|
1189
1329
|
emit(event) {
|
|
1330
|
+
if (event.type === "turn_completed" || event.type === "turn_cancelled" || event.type === "turn_failed") {
|
|
1331
|
+
for (const [requestId, pending] of this.modeChanges) {
|
|
1332
|
+
if (pending.sessionId === event.sessionId && pending.turnId === event.turnId)
|
|
1333
|
+
this.modeChanges.delete(requestId);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1190
1336
|
for (const listener of this.listeners)
|
|
1191
1337
|
listener(event);
|
|
1192
1338
|
}
|
|
@@ -1194,6 +1340,7 @@ export class CodexRuntime {
|
|
|
1194
1340
|
this.unsubscribe?.();
|
|
1195
1341
|
this.unsubscribe = undefined;
|
|
1196
1342
|
this.attachedClient = undefined;
|
|
1343
|
+
this.modeChanges.clear();
|
|
1197
1344
|
for (const session of this.sessions.values()) {
|
|
1198
1345
|
session.needsResume = true;
|
|
1199
1346
|
const turnId = session.activeTurnId;
|
|
@@ -1285,11 +1432,12 @@ function turnStartedAt(turn) {
|
|
|
1285
1432
|
return typeof turn.startedAt === "number" ? turn.startedAt * 1_000 : Date.now();
|
|
1286
1433
|
}
|
|
1287
1434
|
function extractFinalResponse(turn) {
|
|
1435
|
+
return finalResponseMessages(turn).map((item) => item.text.trim()).join("\n\n");
|
|
1436
|
+
}
|
|
1437
|
+
function finalResponseMessages(turn) {
|
|
1288
1438
|
const messages = (turn.items ?? []).filter((item) => item.type === "agentMessage" && typeof item.text === "string" && item.text.trim());
|
|
1289
1439
|
const finalMessages = messages.filter((item) => item.phase === "final_answer");
|
|
1290
|
-
return
|
|
1291
|
-
.map((item) => item.text.trim())
|
|
1292
|
-
.join("\n\n");
|
|
1440
|
+
return finalMessages.length ? finalMessages : messages.filter((item) => item.phase !== "commentary").slice(-1);
|
|
1293
1441
|
}
|
|
1294
1442
|
function extractGeneratedImagePaths(turn) {
|
|
1295
1443
|
return uniqueStrings((turn.items ?? []).flatMap((item) => {
|
|
@@ -1353,7 +1501,7 @@ function remoteSessionSummary(thread) {
|
|
|
1353
1501
|
finalResponse: lastTurn && lastTurn.status !== "inProgress"
|
|
1354
1502
|
? appendGeneratedImageMarkdown(extractFinalResponse(lastTurn), extractGeneratedImagePaths(lastTurn)) || undefined
|
|
1355
1503
|
: undefined,
|
|
1356
|
-
lastError: lastTurn?.error
|
|
1504
|
+
lastError: formatCodexError(lastTurn?.error),
|
|
1357
1505
|
lastTurnToolCount: toolCounts?.total,
|
|
1358
1506
|
lastTurnCompletedToolCount: toolCounts?.completed,
|
|
1359
1507
|
lastTurnFailedToolCount: toolCounts?.failed,
|