@keyou007/agent-bot 0.1.24-alpha.7 → 0.1.24
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 +28 -0
- package/README.md +9 -5
- package/README.zh.md +9 -5
- package/dist/codex/CodexProcessManager.js +3 -1
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +165 -23
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/codex/CodexVersion.js +2 -2
- package/dist/codex/CodexVersion.js.map +1 -1
- package/dist/codex/ProviderSwitch.js +2 -2
- package/dist/codex/ProviderSwitch.js.map +1 -1
- package/dist/feishu/CardRenderer.js +10 -6
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +7 -3
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +26 -10
- package/dist/local-files/TurnPreviewPage.js.map +1 -1
- package/dist/presentation/visibleTurnActivities.js +13 -0
- package/dist/presentation/visibleTurnActivities.js.map +1 -0
- package/dist/proxy/ProxySessionController.js +84 -17
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/runtime/responseErrors.js +60 -0
- package/dist/runtime/responseErrors.js.map +1 -0
- package/docs/technical-reference.md +11 -1
- package/docs/technical-reference.zh.md +11 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +5 -3
- package/src/codex/CodexProcessManager.ts +3 -1
- package/src/codex/CodexRuntime.ts +158 -22
- package/src/codex/CodexVersion.ts +2 -2
- package/src/codex/ProviderSwitch.ts +2 -2
- package/src/feishu/CardRenderer.ts +10 -5
- package/src/feishu/FeishuConnector.ts +7 -3
- package/src/local-files/TurnPreviewPage.ts +25 -10
- package/src/presentation/visibleTurnActivities.ts +12 -0
- package/src/proxy/ProxySessionController.ts +90 -20
- package/src/runtime/responseErrors.ts +62 -0
- package/src/runtime/types.ts +7 -1
|
@@ -21,6 +21,7 @@ import { classifyFileContent } from "../local-files/LocalFileViewerServer.js";
|
|
|
21
21
|
import { errorLogValue } from "../logging/errorLogValue.js";
|
|
22
22
|
import { createTurnViewState, hydrateTurnViewState } from "../presentation/TurnStateReducer.js";
|
|
23
23
|
import { buildTurnGraphRows } from "../presentation/turnGraph.js";
|
|
24
|
+
import { displayRuntimeError, isResponseContinuationError } from "../runtime/responseErrors.js";
|
|
24
25
|
import { isActiveShellCommandJob, ShellCommandJobManager, } from "../shell/ShellCommandJobManager.js";
|
|
25
26
|
import { SessionOperationQueue } from "./SessionOperationQueue.js";
|
|
26
27
|
import { createId } from "../utils/id.js";
|
|
@@ -245,6 +246,7 @@ export class ProxySessionController {
|
|
|
245
246
|
lastSessionListings = new Map();
|
|
246
247
|
threadInitializations = new Map();
|
|
247
248
|
llmRetryingSessions = new Set();
|
|
249
|
+
responseContinuations = new Map();
|
|
248
250
|
planHandoffs = new Map();
|
|
249
251
|
retriedFailureTurnIds = new Set();
|
|
250
252
|
queuedPromptRetryTimers = new Map();
|
|
@@ -3132,6 +3134,16 @@ export class ProxySessionController {
|
|
|
3132
3134
|
}
|
|
3133
3135
|
async startTurn(loaded, text, replyTarget, localImagePaths, options = {}) {
|
|
3134
3136
|
return this.settingsOperations.run(loaded.record.localSessionId, async () => {
|
|
3137
|
+
if (options.responseContinuationTurnId) {
|
|
3138
|
+
const current = this.store.getSession(loaded.record.localSessionId);
|
|
3139
|
+
if (!current || current.status === "closed" || current.lastTurnId !== options.responseContinuationTurnId
|
|
3140
|
+
|| this.sessionResets.has(current.localSessionId)
|
|
3141
|
+
|| this.responseContinuations.get(current.localSessionId)?.turnId !== options.responseContinuationTurnId
|
|
3142
|
+
|| this.store.getPendingProviderSettings(current.localSessionId)) {
|
|
3143
|
+
throw new Error("任务或运行设置已变化,未自动恢复模型续接。请发送消息继续。");
|
|
3144
|
+
}
|
|
3145
|
+
return this.startTurnNow(loaded, text, replyTarget, localImagePaths, options);
|
|
3146
|
+
}
|
|
3135
3147
|
try {
|
|
3136
3148
|
await this.applyPendingProviderSettings(loaded);
|
|
3137
3149
|
}
|
|
@@ -3196,15 +3208,26 @@ export class ProxySessionController {
|
|
|
3196
3208
|
|| this.sessionResets.has(loaded.record.localSessionId))) {
|
|
3197
3209
|
throw new Error("方案执行已取消。");
|
|
3198
3210
|
}
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3211
|
+
if (options.responseContinuationTurnId) {
|
|
3212
|
+
const recovery = this.responseContinuations.get(loaded.record.localSessionId);
|
|
3213
|
+
if (recovery?.turnId !== options.responseContinuationTurnId
|
|
3214
|
+
|| this.sessionResets.has(loaded.record.localSessionId) || !loaded.runtime.recoverResponseContinuation) {
|
|
3215
|
+
throw new Error("模型续接恢复已取消。");
|
|
3216
|
+
}
|
|
3217
|
+
turnId = await loaded.runtime.recoverResponseContinuation(loaded.record.localSessionId, options.responseContinuationTurnId, recovery.abort.signal);
|
|
3218
|
+
}
|
|
3219
|
+
else {
|
|
3220
|
+
turnId = await loaded.runtime.startTurn(loaded.record.localSessionId, options.approvedPlanTurnId
|
|
3221
|
+
? { text: runtimeText, collaborationMode: "default" }
|
|
3222
|
+
: runtimePrompt(runtimeText, localImagePaths));
|
|
3223
|
+
}
|
|
3202
3224
|
break;
|
|
3203
3225
|
}
|
|
3204
3226
|
catch (error) {
|
|
3205
3227
|
const message = error instanceof Error ? error.message : String(error);
|
|
3206
3228
|
const attempt = this.store.getTurnAttempt(attemptId);
|
|
3207
3229
|
const retryAttempt = attempt
|
|
3230
|
+
&& !options.responseContinuationTurnId
|
|
3208
3231
|
&& isRetryableLlmTurnFailure(message)
|
|
3209
3232
|
&& attempt.retryCount < MAX_LLM_TURN_RETRIES
|
|
3210
3233
|
? this.store.prepareUnstartedTurnAttemptRetry(attemptId)
|
|
@@ -3770,7 +3793,23 @@ export class ProxySessionController {
|
|
|
3770
3793
|
if (event.type === "turn_failed") {
|
|
3771
3794
|
if (this.retriedFailureTurnIds.has(event.turnId))
|
|
3772
3795
|
return;
|
|
3773
|
-
|
|
3796
|
+
const source = this.store.getSession(event.sessionId);
|
|
3797
|
+
const responseContinuation = failedAttempt?.retryCount === 0 && failedAttempt.status !== "cancelling"
|
|
3798
|
+
&& source && source.status !== "closed" && source.lastTurnId === event.turnId
|
|
3799
|
+
&& !this.sessionResets.has(event.sessionId) && !this.store.getPendingProviderSettings(event.sessionId)
|
|
3800
|
+
&& Boolean(this.runtimes.forAgent(source.agentName).recoverResponseContinuation)
|
|
3801
|
+
&& isResponseContinuationError(event.message);
|
|
3802
|
+
if (failedAttempt && responseContinuation) {
|
|
3803
|
+
const retryAttempt = this.store.prepareTurnAttemptRetry(failedAttempt.attemptId, event.turnId);
|
|
3804
|
+
if (retryAttempt) {
|
|
3805
|
+
this.retriedFailureTurnIds.add(event.turnId);
|
|
3806
|
+
this.responseContinuations.set(event.sessionId, { turnId: event.turnId, abort: new AbortController() });
|
|
3807
|
+
await this.retryFailedLlmTurn(event, retryAttempt, true);
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
if (failedAttempt && !this.responseContinuations.has(event.sessionId)
|
|
3812
|
+
&& isRetryableLlmTurnFailure(event.message) && failedAttempt.retryCount < MAX_LLM_TURN_RETRIES) {
|
|
3774
3813
|
const retryAttempt = this.store.prepareTurnAttemptRetry(failedAttempt.attemptId, event.turnId);
|
|
3775
3814
|
if (retryAttempt) {
|
|
3776
3815
|
this.retriedFailureTurnIds.add(event.turnId);
|
|
@@ -3923,7 +3962,7 @@ export class ProxySessionController {
|
|
|
3923
3962
|
await this.scheduleNextQueuedPrompt(sessionId);
|
|
3924
3963
|
}
|
|
3925
3964
|
}
|
|
3926
|
-
async retryFailedLlmTurn(event, attempt) {
|
|
3965
|
+
async retryFailedLlmTurn(event, attempt, responseContinuation = false) {
|
|
3927
3966
|
const retryNumber = attempt.retryCount;
|
|
3928
3967
|
const session = this.store.getSession(event.sessionId);
|
|
3929
3968
|
if (!session || session.status === "closed") {
|
|
@@ -3939,13 +3978,16 @@ export class ProxySessionController {
|
|
|
3939
3978
|
attemptId: attempt.attemptId,
|
|
3940
3979
|
failedTurnId: event.turnId,
|
|
3941
3980
|
retryNumber,
|
|
3942
|
-
maxRetries: MAX_LLM_TURN_RETRIES,
|
|
3981
|
+
maxRetries: responseContinuation ? 1 : MAX_LLM_TURN_RETRIES,
|
|
3982
|
+
responseContinuation,
|
|
3943
3983
|
error: event.message,
|
|
3944
3984
|
});
|
|
3945
3985
|
try {
|
|
3946
3986
|
await this.outbound.onEvent({
|
|
3947
3987
|
...event,
|
|
3948
|
-
message:
|
|
3988
|
+
message: responseContinuation
|
|
3989
|
+
? `${event.message}\n\n模型续接状态失效,正在恢复连接并继续(1/1)。`
|
|
3990
|
+
: `${event.message}\n\n检测到临时模型服务错误,正在自动重试(${retryNumber}/${MAX_LLM_TURN_RETRIES})。`,
|
|
3949
3991
|
});
|
|
3950
3992
|
}
|
|
3951
3993
|
catch (error) {
|
|
@@ -3957,25 +3999,36 @@ export class ProxySessionController {
|
|
|
3957
3999
|
try {
|
|
3958
4000
|
const current = this.store.getSession(session.localSessionId) ?? session;
|
|
3959
4001
|
const loaded = await this.loadSession(current, Boolean(this.store.getPendingProviderSettings(current.localSessionId)));
|
|
3960
|
-
const retryTurnId = await this.startTurn(loaded, llmRetryRuntimePrompt(attempt.promptText, retryNumber), replyTarget, attempt.localImagePaths, {
|
|
4002
|
+
const retryTurnId = await this.startTurn(loaded, responseContinuation ? "" : llmRetryRuntimePrompt(attempt.promptText, retryNumber), replyTarget, responseContinuation ? undefined : attempt.localImagePaths, {
|
|
3961
4003
|
attemptId: attempt.attemptId,
|
|
3962
4004
|
messageId: attempt.messageId,
|
|
3963
|
-
displayPrompt: llmRetryCardPrompt(attempt.promptText, retryNumber),
|
|
4005
|
+
displayPrompt: responseContinuation ? `恢复模型续接:${attempt.promptText}` : llmRetryCardPrompt(attempt.promptText, retryNumber),
|
|
3964
4006
|
preserveAttemptOnFailure: true,
|
|
4007
|
+
...(responseContinuation ? { responseContinuationTurnId: event.turnId } : {}),
|
|
3965
4008
|
});
|
|
3966
4009
|
this.store.rebindPendingTurnMessages(session.localSessionId, event.turnId, retryTurnId);
|
|
3967
4010
|
}
|
|
3968
4011
|
catch (error) {
|
|
3969
|
-
this.
|
|
3970
|
-
this.store.
|
|
3971
|
-
|
|
4012
|
+
const aborted = responseContinuation && !this.responseContinuations.has(session.localSessionId);
|
|
4013
|
+
this.store.updateTurnAttempt(attempt.attemptId, { status: aborted ? "cancelled" : "failed" });
|
|
4014
|
+
const latest = this.store.getSession(session.localSessionId);
|
|
4015
|
+
if (!responseContinuation || (latest?.lastTurnId === event.turnId && latest.status !== "closed")) {
|
|
4016
|
+
this.store.updateSession(session.localSessionId, { status: aborted ? "ready" : "failed" });
|
|
4017
|
+
}
|
|
4018
|
+
await this.finalizeTurnMessageReactions(event.turnId, aborted ? "cancelled" : "failed").catch((reactionError) => {
|
|
3972
4019
|
this.logger.warn({ error: reactionError, sessionId: session.localSessionId, turnId: event.turnId }, "Failed to finalize reactions after an LLM retry could not start.");
|
|
3973
4020
|
});
|
|
3974
4021
|
this.logger.error({ error, sessionId: session.localSessionId, retryNumber }, "Failed to start an automatic LLM turn retry.");
|
|
4022
|
+
if (responseContinuation && !aborted) {
|
|
4023
|
+
await this.outbound.sendText(attempt.contextKey, `模型续接恢复未能启动:${displayRuntimeError(runtimeErrorMessage(error))}。请发送消息继续。`);
|
|
4024
|
+
}
|
|
3975
4025
|
await this.scheduleNextQueuedPrompt(session.localSessionId);
|
|
3976
4026
|
}
|
|
3977
4027
|
finally {
|
|
3978
4028
|
this.llmRetryingSessions.delete(session.localSessionId);
|
|
4029
|
+
if (this.responseContinuations.get(session.localSessionId)?.turnId === event.turnId) {
|
|
4030
|
+
this.responseContinuations.delete(session.localSessionId);
|
|
4031
|
+
}
|
|
3979
4032
|
}
|
|
3980
4033
|
}
|
|
3981
4034
|
scheduleNextQueuedPrompt(sessionId) {
|
|
@@ -4213,6 +4266,8 @@ export class ProxySessionController {
|
|
|
4213
4266
|
await this.cancelSession(record);
|
|
4214
4267
|
}
|
|
4215
4268
|
async cancelSession(record) {
|
|
4269
|
+
this.responseContinuations.get(record.localSessionId)?.abort.abort();
|
|
4270
|
+
this.responseContinuations.delete(record.localSessionId);
|
|
4216
4271
|
this.planHandoffs.delete(record.localSessionId);
|
|
4217
4272
|
const runtime = this.runtimes.forAgent(record.agentName);
|
|
4218
4273
|
if (runtime.kind === "codex" && record.remoteSessionId && runtime.interruptRemoteTurn) {
|
|
@@ -4290,6 +4345,8 @@ export class ProxySessionController {
|
|
|
4290
4345
|
});
|
|
4291
4346
|
}
|
|
4292
4347
|
async runSessionReset(sessionId, operation) {
|
|
4348
|
+
this.responseContinuations.get(sessionId)?.abort.abort();
|
|
4349
|
+
this.responseContinuations.delete(sessionId);
|
|
4293
4350
|
this.planHandoffs.delete(sessionId);
|
|
4294
4351
|
const previous = this.sessionResets.get(sessionId) ?? Promise.resolve();
|
|
4295
4352
|
const reset = previous.catch(() => undefined).then(operation);
|
|
@@ -4671,9 +4728,16 @@ export class ProxySessionController {
|
|
|
4671
4728
|
throw new Error("当前运行时不支持 Provider 设置。");
|
|
4672
4729
|
loaded.session = loaded.runtime.getSession(loaded.record.localSessionId) ?? loaded.session;
|
|
4673
4730
|
const remoteSessionId = loaded.session.remoteSessionId;
|
|
4674
|
-
const persist = (candidate) => this.agentDefaultsOperations.run(loaded.record.agentName, async () => {
|
|
4731
|
+
const persist = (candidate, replacement) => this.agentDefaultsOperations.run(loaded.record.agentName, async () => {
|
|
4675
4732
|
if (candidate.remoteSessionId !== remoteSessionId) {
|
|
4676
|
-
|
|
4733
|
+
const saved = this.store.getSession(loaded.record.localSessionId);
|
|
4734
|
+
if (replacement?.emptyRemoteSessionId !== remoteSessionId || !saved || saved.remoteSessionId !== remoteSessionId
|
|
4735
|
+
|| saved.status === "closed" || saved.status === "running" || saved.lastTurnId || loaded.session.activeTurnId || candidate.activeTurnId
|
|
4736
|
+
|| candidate.localSessionId !== saved.localSessionId || !candidate.remoteSessionId
|
|
4737
|
+
|| candidate.agentName !== saved.agentName || candidate.cwd !== saved.cwd || candidate.runtimeKind !== loaded.session.runtimeKind
|
|
4738
|
+
|| this.store.hasTurnAttemptsForSession(saved.localSessionId) || this.store.getForkHistorySource(saved.localSessionId)) {
|
|
4739
|
+
throw new Error("设置切换返回了不同的任务 ID,未保存设置;仅允许已验证的空会话更换底层任务。");
|
|
4740
|
+
}
|
|
4677
4741
|
}
|
|
4678
4742
|
assertProviderSettingsApplied(candidate, settings);
|
|
4679
4743
|
const agent = this.ensureAgent(loaded.record.agentName);
|
|
@@ -4681,6 +4745,7 @@ export class ProxySessionController {
|
|
|
4681
4745
|
await this.persistAgentExecutionDefaultsNow(loaded.record.localSessionId, candidate);
|
|
4682
4746
|
try {
|
|
4683
4747
|
this.store.updateRuntimeSession(loaded.record.localSessionId, {
|
|
4748
|
+
remoteSessionId: candidate.remoteSessionId,
|
|
4684
4749
|
modelProvider: candidate.modelProvider,
|
|
4685
4750
|
model: candidate.model,
|
|
4686
4751
|
reasoningEffort: candidate.reasoningEffort,
|
|
@@ -4694,6 +4759,7 @@ export class ProxySessionController {
|
|
|
4694
4759
|
}
|
|
4695
4760
|
});
|
|
4696
4761
|
const session = await loaded.runtime.setExecutionSettings(loaded.record.localSessionId, settings, persist);
|
|
4762
|
+
loaded.record = this.store.getSession(loaded.record.localSessionId) ?? loaded.record;
|
|
4697
4763
|
this.settingsOnlySessions.delete(loaded.record.localSessionId);
|
|
4698
4764
|
this.outbound.updateSessionModel(loaded.record.localSessionId, session.model, session.modelProvider);
|
|
4699
4765
|
return session;
|
|
@@ -5858,6 +5924,7 @@ export class ProxySessionController {
|
|
|
5858
5924
|
throw new Error(`这不是目录:${directory}`);
|
|
5859
5925
|
}
|
|
5860
5926
|
async createDirectoryFromCard(contextKey, action) {
|
|
5927
|
+
const updateMessageId = requiredCardMessageId(action.messageId);
|
|
5861
5928
|
const directory = directoryActionPath(action.value.directory);
|
|
5862
5929
|
await this.assertBrowsableDirectory(directory);
|
|
5863
5930
|
const folderName = directoryFolderName(cardFormValue(action.value, "folderName"));
|
|
@@ -5873,9 +5940,9 @@ export class ProxySessionController {
|
|
|
5873
5940
|
throw new Error(`目录已存在:${newDirectory}`);
|
|
5874
5941
|
throw new Error(`无法创建目录 ${newDirectory}:${runtimeErrorMessage(error)}`);
|
|
5875
5942
|
}
|
|
5876
|
-
await this.openDirectoryBrowser(contextKey,
|
|
5877
|
-
updateMessageId
|
|
5878
|
-
page:
|
|
5943
|
+
await this.openDirectoryBrowser(contextKey, newDirectory, {
|
|
5944
|
+
updateMessageId,
|
|
5945
|
+
page: 0,
|
|
5879
5946
|
});
|
|
5880
5947
|
await this.outbound.sendText(contextKey, `已创建目录:${newDirectory}`);
|
|
5881
5948
|
}
|