@keyou007/agent-bot 0.1.16-alpha.3 → 0.1.16-alpha.4
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 +6 -0
- package/dist/cli/LocalControlServer.js +4 -1
- package/dist/cli/LocalControlServer.js.map +1 -1
- package/dist/feishu/CardRenderer.js +60 -8
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +108 -14
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/startup/SessionMetadataHydrator.js +0 -25
- package/dist/startup/SessionMetadataHydrator.js.map +1 -1
- package/dist/state/StateStore.js +32 -0
- package/dist/state/StateStore.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/cli/LocalControlServer.ts +3 -1
- package/src/feishu/CardRenderer.ts +61 -7
- package/src/proxy/ProxySessionController.ts +126 -26
- package/src/startup/SessionMetadataHydrator.ts +0 -27
- package/src/state/StateStore.ts +34 -0
|
@@ -28,7 +28,7 @@ const SESSION_PROMPT_PREVIEW_LENGTH = 50;
|
|
|
28
28
|
const DIRECTORY_PAGE_SIZE = 15;
|
|
29
29
|
const RESET_HISTORY_PAGE_SIZE = 10;
|
|
30
30
|
const MAX_LLM_TURN_RETRIES = 3;
|
|
31
|
-
const RECOVERY_ACTIVITY_WINDOW_MS =
|
|
31
|
+
const RECOVERY_ACTIVITY_WINDOW_MS = 10 * 60 * 1_000;
|
|
32
32
|
const RECOVERY_HEARTBEAT_INTERVAL_MS = 60 * 1_000;
|
|
33
33
|
const SHELL_COMMAND_JOB_POLL_INTERVAL_MS = 1_000;
|
|
34
34
|
const SHELL_COMMAND_JOB_RELAUNCH_DELAY_MS = 5_000;
|
|
@@ -1439,6 +1439,7 @@ export class ProxySessionController {
|
|
|
1439
1439
|
return recovery;
|
|
1440
1440
|
}
|
|
1441
1441
|
async runStartupRecovery() {
|
|
1442
|
+
await this.finishPersistedCancellationRequests();
|
|
1442
1443
|
await this.recoverShellCommandJobs();
|
|
1443
1444
|
this.backfillTurnAttemptsForUpgrade();
|
|
1444
1445
|
const attempts = this.store.listIncompleteTurnAttempts();
|
|
@@ -1464,6 +1465,31 @@ export class ProxySessionController {
|
|
|
1464
1465
|
}
|
|
1465
1466
|
this.restorePersistedQueuedPrompts();
|
|
1466
1467
|
}
|
|
1468
|
+
async finishPersistedCancellationRequests() {
|
|
1469
|
+
for (const attempt of this.store.listCancellationRequestedTurnAttempts()) {
|
|
1470
|
+
const session = this.store.getSession(attempt.localSessionId);
|
|
1471
|
+
if (!attempt.turnId) {
|
|
1472
|
+
this.store.updateTurnAttempt(attempt.attemptId, { status: "cancelled" });
|
|
1473
|
+
if (session?.status === "starting" || session?.status === "running") {
|
|
1474
|
+
this.store.updateSession(session.localSessionId, { status: "ready" });
|
|
1475
|
+
}
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1478
|
+
if (session) {
|
|
1479
|
+
this.outbound.registerSession(session.localSessionId, attempt.contextKey, session.title, session.cwd, this.agentLabel(session.agentName));
|
|
1480
|
+
}
|
|
1481
|
+
try {
|
|
1482
|
+
await this.handleRuntimeEvent({
|
|
1483
|
+
type: "turn_cancelled",
|
|
1484
|
+
sessionId: attempt.localSessionId,
|
|
1485
|
+
turnId: attempt.turnId,
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
catch (error) {
|
|
1489
|
+
this.logger.warn({ error, attemptId: attempt.attemptId, turnId: attempt.turnId }, "Failed to finish a persisted user-requested turn cancellation.");
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1467
1493
|
backfillTurnAttemptsForUpgrade() {
|
|
1468
1494
|
for (const session of this.store.listAllSessions()) {
|
|
1469
1495
|
if (session.status !== "starting" && session.status !== "running" && session.status !== "ready")
|
|
@@ -1521,7 +1547,7 @@ export class ProxySessionController {
|
|
|
1521
1547
|
const session = this.store.getSession(attempt.localSessionId);
|
|
1522
1548
|
const oldCardTurnId = attempt.pendingTurnId ?? attempt.turnId;
|
|
1523
1549
|
if (session && oldCardTurnId && this.outbound.canRoute(attempt.contextKey)) {
|
|
1524
|
-
await this.outbound.interruptTurnForRecovery(session.localSessionId, attempt.contextKey, oldCardTurnId, "执行中断已超过
|
|
1550
|
+
await this.outbound.interruptTurnForRecovery(session.localSessionId, attempt.contextKey, oldCardTurnId, "执行中断已超过 10 分钟,未自动恢复。").catch((error) => {
|
|
1525
1551
|
this.logger.warn({ error, attemptId: attempt.attemptId, turnId: oldCardTurnId }, "Failed to close an expired interrupted thinking card.");
|
|
1526
1552
|
});
|
|
1527
1553
|
}
|
|
@@ -2382,17 +2408,68 @@ export class ProxySessionController {
|
|
|
2382
2408
|
});
|
|
2383
2409
|
}
|
|
2384
2410
|
let turnId;
|
|
2411
|
+
let runtimeText = text;
|
|
2412
|
+
let cardPrompt = options.displayPrompt ?? text;
|
|
2413
|
+
let retriedStart = false;
|
|
2385
2414
|
try {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2415
|
+
while (true) {
|
|
2416
|
+
let pendingTurnId;
|
|
2417
|
+
try {
|
|
2418
|
+
pendingTurnId = await this.outbound.startPendingTurn(loaded.record.localSessionId, loaded.record.contextKey, title, replyTarget, cardPrompt);
|
|
2419
|
+
}
|
|
2420
|
+
catch (error) {
|
|
2421
|
+
this.store.updateTurnAttempt(attemptId, {
|
|
2422
|
+
status: options.preserveAttemptOnFailure ? "recovering" : "failed",
|
|
2423
|
+
});
|
|
2424
|
+
throw error;
|
|
2425
|
+
}
|
|
2426
|
+
this.store.updateTurnAttempt(attemptId, { pendingTurnId: pendingTurnId ?? null });
|
|
2427
|
+
try {
|
|
2428
|
+
turnId = await loaded.runtime.startTurn(loaded.record.localSessionId, runtimePrompt(runtimeText, localImagePaths));
|
|
2429
|
+
break;
|
|
2430
|
+
}
|
|
2431
|
+
catch (error) {
|
|
2432
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2433
|
+
const attempt = this.store.getTurnAttempt(attemptId);
|
|
2434
|
+
const retryAttempt = attempt
|
|
2435
|
+
&& isRetryableLlmTurnFailure(message)
|
|
2436
|
+
&& attempt.retryCount < MAX_LLM_TURN_RETRIES
|
|
2437
|
+
? this.store.prepareUnstartedTurnAttemptRetry(attemptId)
|
|
2438
|
+
: undefined;
|
|
2439
|
+
if (!retryAttempt) {
|
|
2440
|
+
const exhausted = attempt
|
|
2441
|
+
&& isRetryableLlmTurnFailure(message)
|
|
2442
|
+
&& attempt.retryCount >= MAX_LLM_TURN_RETRIES;
|
|
2443
|
+
const finalMessage = exhausted
|
|
2444
|
+
? `${message}\n\n已自动重试 ${MAX_LLM_TURN_RETRIES} 次,仍未成功。`
|
|
2445
|
+
: message;
|
|
2446
|
+
this.store.updateTurnAttempt(attemptId, {
|
|
2447
|
+
status: options.preserveAttemptOnFailure ? "recovering" : "failed",
|
|
2448
|
+
});
|
|
2449
|
+
await this.outbound.failPendingTurn(loaded.record.localSessionId, finalMessage);
|
|
2450
|
+
if (finalMessage === message)
|
|
2451
|
+
throw error;
|
|
2452
|
+
throw new Error(finalMessage, { cause: error });
|
|
2453
|
+
}
|
|
2454
|
+
retriedStart = true;
|
|
2455
|
+
this.llmRetryingSessions.add(loaded.record.localSessionId);
|
|
2456
|
+
this.store.audit(loaded.record.contextKey, "llm_turn_start_retry_started", {
|
|
2457
|
+
attemptId,
|
|
2458
|
+
retryNumber: retryAttempt.retryCount,
|
|
2459
|
+
maxRetries: MAX_LLM_TURN_RETRIES,
|
|
2460
|
+
error: message,
|
|
2461
|
+
});
|
|
2462
|
+
await this.outbound.failPendingTurn(loaded.record.localSessionId, `${message}\n\n检测到临时模型服务错误,正在自动重试(${retryAttempt.retryCount}/${MAX_LLM_TURN_RETRIES})。`).catch((presentationError) => {
|
|
2463
|
+
this.logger.warn({ error: presentationError, sessionId: loaded.record.localSessionId }, "Failed to finalize the pending thinking card before an LLM start retry.");
|
|
2464
|
+
});
|
|
2465
|
+
runtimeText = llmRetryRuntimePrompt(retryAttempt.promptText, retryAttempt.retryCount);
|
|
2466
|
+
cardPrompt = llmRetryCardPrompt(options.displayPrompt ?? retryAttempt.promptText, retryAttempt.retryCount);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2389
2469
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
});
|
|
2394
|
-
await this.outbound.failPendingTurn(loaded.record.localSessionId, error instanceof Error ? error.message : String(error));
|
|
2395
|
-
throw error;
|
|
2470
|
+
finally {
|
|
2471
|
+
if (retriedStart)
|
|
2472
|
+
this.llmRetryingSessions.delete(loaded.record.localSessionId);
|
|
2396
2473
|
}
|
|
2397
2474
|
const attempt = this.store.getTurnAttempt(attemptId);
|
|
2398
2475
|
this.store.updateTurnAttempt(attemptId, {
|
|
@@ -3043,7 +3120,15 @@ export class ProxySessionController {
|
|
|
3043
3120
|
await this.outbound.sendText(record.contextKey, "当前没有正在执行的任务。");
|
|
3044
3121
|
return;
|
|
3045
3122
|
}
|
|
3046
|
-
|
|
3123
|
+
const cancellationAttempt = this.store.requestTurnAttemptCancellation(turnId);
|
|
3124
|
+
try {
|
|
3125
|
+
await runtime.interruptRemoteTurn(record.remoteSessionId, turnId);
|
|
3126
|
+
}
|
|
3127
|
+
catch (error) {
|
|
3128
|
+
if (cancellationAttempt)
|
|
3129
|
+
this.store.restoreTurnAttemptAfterCancellationFailure(cancellationAttempt);
|
|
3130
|
+
throw error;
|
|
3131
|
+
}
|
|
3047
3132
|
this.store.audit(record.contextKey, "turn_interrupt_sent", {
|
|
3048
3133
|
localSessionId: record.localSessionId,
|
|
3049
3134
|
remoteSessionId: record.remoteSessionId,
|
|
@@ -3058,7 +3143,15 @@ export class ProxySessionController {
|
|
|
3058
3143
|
await this.outbound.sendText(record.contextKey, "当前没有正在执行的任务。");
|
|
3059
3144
|
return;
|
|
3060
3145
|
}
|
|
3061
|
-
|
|
3146
|
+
const cancellationAttempt = this.store.requestTurnAttemptCancellation(turnId);
|
|
3147
|
+
try {
|
|
3148
|
+
await loaded.runtime.cancelTurn(record.localSessionId, turnId);
|
|
3149
|
+
}
|
|
3150
|
+
catch (error) {
|
|
3151
|
+
if (cancellationAttempt)
|
|
3152
|
+
this.store.restoreTurnAttemptAfterCancellationFailure(cancellationAttempt);
|
|
3153
|
+
throw error;
|
|
3154
|
+
}
|
|
3062
3155
|
}
|
|
3063
3156
|
async resetCurrentSessionToTurn(contextKey, sessionId, turnId) {
|
|
3064
3157
|
if (!sessionId || !turnId)
|
|
@@ -5756,7 +5849,8 @@ function isRetryableLlmTurnFailure(message) {
|
|
|
5756
5849
|
/service unavailable|internal server error|bad gateway|gateway timeout/u,
|
|
5757
5850
|
/(?:^|\D)(?:500|502|503|504)(?:\D|$)|upstream (?:error|failure|timeout)/u,
|
|
5758
5851
|
/deadline exceeded|request timed out|request timeout|response timeout/u,
|
|
5759
|
-
/connection (?:reset|closed|aborted)|network error|socket hang up/u,
|
|
5852
|
+
/connection (?:reset|closed|aborted|disconnected)|network error|socket hang up/u,
|
|
5853
|
+
/app server .*(?:closed|disconnected|exited|reset|aborted)/u,
|
|
5760
5854
|
/stream (?:closed|disconnected|interrupted|error)/u,
|
|
5761
5855
|
/\b(?:llm|model|provider|inference)\b.*\b(?:error|failed|failure|unavailable|timeout|timed out)\b/u,
|
|
5762
5856
|
].some((pattern) => pattern.test(normalized));
|