@keyou007/agent-bot 0.1.16 → 0.1.17-alpha.0
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 +8 -0
- package/README.md +4 -2
- package/README.zh.md +4 -2
- package/config.example.yaml +8 -0
- package/dist/cli/help.js +4 -4
- package/dist/cli/taskTarget.js +7 -0
- package/dist/cli/taskTarget.js.map +1 -1
- package/dist/cli.js +12 -12
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexRuntime.js +40 -12
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/config/AgentExecutionDefaultsStore.js +71 -0
- package/dist/config/AgentExecutionDefaultsStore.js.map +1 -0
- package/dist/config/loadConfig.js +8 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +7 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/CardRenderer.js +29 -27
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +255 -30
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +62 -9
- package/dist/state/StateStore.js.map +1 -1
- package/dist/supervision/restartNotificationTarget.js +9 -0
- package/dist/supervision/restartNotificationTarget.js.map +1 -0
- package/docs/technical-reference.md +7 -3
- package/docs/technical-reference.zh.md +7 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +3 -1
- package/src/cli/help.ts +4 -4
- package/src/cli/taskTarget.ts +11 -0
- package/src/cli.ts +16 -20
- package/src/codex/CodexRuntime.ts +38 -10
- package/src/config/AgentExecutionDefaultsStore.ts +71 -0
- package/src/config/loadConfig.ts +8 -0
- package/src/config/schema.ts +13 -1
- package/src/feishu/CardRenderer.ts +30 -22
- package/src/index.ts +17 -2
- package/src/proxy/ProxySessionController.ts +330 -40
- package/src/runtime/types.ts +8 -0
- package/src/state/StateStore.ts +100 -9
- package/src/supervision/restartNotificationTarget.ts +12 -0
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { createProjectlessWorkspace, detectProjectlessWorkspace } from "../codex/ProjectlessWorkspace.js";
|
|
6
6
|
import { resolveUserPath } from "../config/paths.js";
|
|
7
|
-
import { DEFAULT_GROUP_NAME_FORMAT } from "../config/schema.js";
|
|
7
|
+
import { DEFAULT_GROUP_NAME_FORMAT, } from "../config/schema.js";
|
|
8
8
|
import { CommandRouter } from "../commands/CommandRouter.js";
|
|
9
9
|
import { baseChatContextKey, isThreadContextKey } from "../feishu/contextKey.js";
|
|
10
10
|
import { CardRenderer, } from "../feishu/CardRenderer.js";
|
|
@@ -189,6 +189,7 @@ export class ProxySessionController {
|
|
|
189
189
|
queuedPromptCards = new Map();
|
|
190
190
|
queuedPromptCardWrites = new Map();
|
|
191
191
|
sessionResets = new Map();
|
|
192
|
+
resetHistoryOperations = new Map();
|
|
192
193
|
lastSessionListings = new Map();
|
|
193
194
|
threadInitializations = new Map();
|
|
194
195
|
llmRetryingSessions = new Set();
|
|
@@ -513,13 +514,12 @@ export class ProxySessionController {
|
|
|
513
514
|
}
|
|
514
515
|
else if (kind === "turn_reset") {
|
|
515
516
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
516
|
-
|
|
517
|
+
const turnId = String(scopedAction.value.turnId ?? "");
|
|
517
518
|
if (scopedAction.value.cardView === "reset_history") {
|
|
518
|
-
await this.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
});
|
|
519
|
+
await this.resetFromHistoryCard(contextKey, sessionId, turnId, requiredCardMessageId(scopedAction.messageId), resetHistoryPageValue(scopedAction.value.page));
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
await this.resetCurrentSessionToTurn(contextKey, sessionId, turnId);
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
525
|
else if (kind === "turn_reset_page") {
|
|
@@ -761,6 +761,7 @@ export class ProxySessionController {
|
|
|
761
761
|
this.queuedPromptCards.clear();
|
|
762
762
|
this.queuedPromptCardWrites.clear();
|
|
763
763
|
this.sessionResets.clear();
|
|
764
|
+
this.resetHistoryOperations.clear();
|
|
764
765
|
for (const pending of this.pendingMergedForwards.values())
|
|
765
766
|
pending.resolveAttachment(undefined);
|
|
766
767
|
this.pendingMergedForwards.clear();
|
|
@@ -1096,6 +1097,8 @@ export class ProxySessionController {
|
|
|
1096
1097
|
this.store.updateRuntimeSession(record.localSessionId, { permissionMode: nextValue });
|
|
1097
1098
|
}
|
|
1098
1099
|
const session = this.store.getSession(record.localSessionId) ?? record;
|
|
1100
|
+
if (setting)
|
|
1101
|
+
await this.persistAgentExecutionDefaults(session.localSessionId);
|
|
1099
1102
|
const models = await loaded.runtime.listModels();
|
|
1100
1103
|
const currentModel = models.find((candidate) => candidate.id === session.model)
|
|
1101
1104
|
?? models.find((candidate) => candidate.isDefault);
|
|
@@ -2178,6 +2181,18 @@ export class ProxySessionController {
|
|
|
2178
2181
|
const model = remote?.model ?? source?.model;
|
|
2179
2182
|
const reasoningEffort = source?.reasoningEffort;
|
|
2180
2183
|
const permissionMode = source?.permissionMode ?? "auto";
|
|
2184
|
+
const sourceTurnPrompt = truncateText(latestUserPromptFromTurnView(snapshot)
|
|
2185
|
+
?? (lastTurnId === latestTurnId ? remote?.lastUserPrompt : undefined)
|
|
2186
|
+
?? remote?.preview
|
|
2187
|
+
?? source?.title
|
|
2188
|
+
?? remote?.title
|
|
2189
|
+
?? "Fork 来源轮次", 1_000);
|
|
2190
|
+
const sourceTurnCompletedAt = snapshot?.completedAt
|
|
2191
|
+
?? snapshot?.startedAt
|
|
2192
|
+
?? (lastTurnId === latestTurnId
|
|
2193
|
+
? latestRemoteTimestamp(remote?.recencyAt, remote?.updatedAt)
|
|
2194
|
+
: undefined)
|
|
2195
|
+
?? parseIsoTimestamp(source?.updatedAt);
|
|
2181
2196
|
return {
|
|
2182
2197
|
source,
|
|
2183
2198
|
sourceLabel,
|
|
@@ -2196,6 +2211,10 @@ export class ProxySessionController {
|
|
|
2196
2211
|
: mapRemoteTurnStatus(remote?.lastTurnStatus)
|
|
2197
2212
|
?? forkedTurnStatus(snapshot?.status)
|
|
2198
2213
|
?? source?.lastTurnStatus,
|
|
2214
|
+
sourceTurnPrompt,
|
|
2215
|
+
sourceTurnStartedAt: snapshot?.startedAt ?? sourceTurnCompletedAt,
|
|
2216
|
+
sourceTurnCompletedAt,
|
|
2217
|
+
sourceCompletedTurns: remote?.completedTurns ?? [],
|
|
2199
2218
|
sourceWasRunning: isRunning,
|
|
2200
2219
|
forkedFromHistoricalTurn,
|
|
2201
2220
|
};
|
|
@@ -2242,6 +2261,9 @@ export class ProxySessionController {
|
|
|
2242
2261
|
|| source.lastTurnStatus === "running"
|
|
2243
2262
|
|| source.status === "running");
|
|
2244
2263
|
const forkTitle = normalizeTaskTitle(requestedTitle) ?? this.store.nextForkTitle(source.title);
|
|
2264
|
+
const sourceTurnCompletedAt = snapshot?.completedAt
|
|
2265
|
+
?? snapshot?.startedAt
|
|
2266
|
+
?? parseIsoTimestamp(source.updatedAt);
|
|
2245
2267
|
return {
|
|
2246
2268
|
source,
|
|
2247
2269
|
sourceLabel,
|
|
@@ -2258,11 +2280,15 @@ export class ProxySessionController {
|
|
|
2258
2280
|
lastTurnStatus: forkedTurnStatus(snapshot?.status)
|
|
2259
2281
|
?? (source.lastTurnId === lastTurnId ? source.lastTurnStatus : undefined)
|
|
2260
2282
|
?? "completed",
|
|
2283
|
+
sourceTurnPrompt: truncateText(latestUserPromptFromTurnView(snapshot) ?? source.title ?? "Fork 来源轮次", 1_000),
|
|
2284
|
+
sourceTurnStartedAt: snapshot?.startedAt ?? sourceTurnCompletedAt,
|
|
2285
|
+
sourceTurnCompletedAt,
|
|
2261
2286
|
sourceWasRunning,
|
|
2262
2287
|
forkedFromHistoricalTurn: sourceWasRunning && source.lastTurnId !== lastTurnId,
|
|
2263
2288
|
};
|
|
2264
2289
|
}
|
|
2265
2290
|
async forkSessionIntoContext(contextKey, plan) {
|
|
2291
|
+
const sourceCompletedTurns = await this.resolveForkSourceCompletedTurns(plan);
|
|
2266
2292
|
const localSessionId = createId("sess");
|
|
2267
2293
|
const record = this.store.createSession({
|
|
2268
2294
|
localSessionId,
|
|
@@ -2297,16 +2323,21 @@ export class ProxySessionController {
|
|
|
2297
2323
|
lastTurnId: plan.lastTurnId,
|
|
2298
2324
|
lastTurnStatus: plan.lastTurnStatus,
|
|
2299
2325
|
});
|
|
2300
|
-
this.store.setCurrentSession(contextKey, localSessionId);
|
|
2301
2326
|
this.outbound.registerSession(localSessionId, contextKey, forked.title ?? plan.forkTitle, plan.cwd, this.agentLabel(plan.agentName));
|
|
2327
|
+
const sourceTurnHistoryCount = this.persistForkSourceHistory(contextKey, localSessionId, plan, sourceCompletedTurns);
|
|
2302
2328
|
this.store.audit(contextKey, "session_forked", {
|
|
2303
2329
|
sourceLocalSessionId: plan.source?.localSessionId,
|
|
2304
2330
|
sourceRemoteSessionId: plan.remoteSessionId,
|
|
2305
2331
|
sourceTurnId: plan.lastTurnId,
|
|
2332
|
+
sourceTurnPrompt: plan.sourceTurnPrompt,
|
|
2333
|
+
sourceTurnStartedAt: plan.sourceTurnStartedAt,
|
|
2334
|
+
sourceTurnCompletedAt: plan.sourceTurnCompletedAt,
|
|
2335
|
+
sourceTurnHistoryCount,
|
|
2306
2336
|
sourceWasRunning: plan.sourceWasRunning,
|
|
2307
2337
|
forkedLocalSessionId: localSessionId,
|
|
2308
2338
|
forkedRemoteSessionId: forked.remoteSessionId,
|
|
2309
2339
|
});
|
|
2340
|
+
this.store.setCurrentSession(contextKey, localSessionId);
|
|
2310
2341
|
return {
|
|
2311
2342
|
record: this.store.getSession(localSessionId) ?? record,
|
|
2312
2343
|
session: forked,
|
|
@@ -2317,6 +2348,122 @@ export class ProxySessionController {
|
|
|
2317
2348
|
throw error;
|
|
2318
2349
|
}
|
|
2319
2350
|
}
|
|
2351
|
+
async resolveForkSourceCompletedTurns(plan) {
|
|
2352
|
+
let completedTurns = plan.sourceCompletedTurns;
|
|
2353
|
+
if (completedTurns === undefined && plan.runtime.readRemoteSession) {
|
|
2354
|
+
try {
|
|
2355
|
+
completedTurns = (await plan.runtime.readRemoteSession(plan.remoteSessionId)).completedTurns ?? [];
|
|
2356
|
+
}
|
|
2357
|
+
catch (error) {
|
|
2358
|
+
this.logger.warn({ error, remoteSessionId: plan.remoteSessionId, sourceTurnId: plan.lastTurnId }, "Failed to read the source task Turn history before Fork; keeping the Fork anchor fallback.");
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
if (!completedTurns?.length)
|
|
2362
|
+
return [];
|
|
2363
|
+
const anchorIndex = completedTurns.findIndex((turn) => turn.id === plan.lastTurnId);
|
|
2364
|
+
if (anchorIndex < 0) {
|
|
2365
|
+
this.logger.warn({ remoteSessionId: plan.remoteSessionId, sourceTurnId: plan.lastTurnId }, "The source task Turn history did not include the Fork anchor; keeping the Fork anchor fallback.");
|
|
2366
|
+
return [];
|
|
2367
|
+
}
|
|
2368
|
+
return completedTurns.slice(0, anchorIndex + 1);
|
|
2369
|
+
}
|
|
2370
|
+
persistForkSourceHistory(contextKey, forkedLocalSessionId, plan, completedTurns) {
|
|
2371
|
+
if (completedTurns.length === 0)
|
|
2372
|
+
return 0;
|
|
2373
|
+
const localSessionId = plan.source?.localSessionId ?? forkedLocalSessionId;
|
|
2374
|
+
const historyContextKey = plan.source?.contextKey ?? contextKey;
|
|
2375
|
+
const fallbackStart = plan.sourceTurnStartedAt
|
|
2376
|
+
?? plan.sourceTurnCompletedAt
|
|
2377
|
+
?? Date.now() - completedTurns.length;
|
|
2378
|
+
return this.persistCompletedTurnHistory({
|
|
2379
|
+
localSessionId,
|
|
2380
|
+
contextKey: historyContextKey,
|
|
2381
|
+
agentName: plan.agentName,
|
|
2382
|
+
remoteSessionId: plan.remoteSessionId,
|
|
2383
|
+
completedTurns,
|
|
2384
|
+
fallbackStart,
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
persistCompletedTurnHistory(input) {
|
|
2388
|
+
try {
|
|
2389
|
+
this.store.importCompletedTurnHistory({
|
|
2390
|
+
localSessionId: input.localSessionId,
|
|
2391
|
+
contextKey: input.contextKey,
|
|
2392
|
+
agentName: input.agentName,
|
|
2393
|
+
remoteSessionId: input.remoteSessionId,
|
|
2394
|
+
turns: input.completedTurns.map((turn, index) => {
|
|
2395
|
+
const startedAt = turn.startedAt ?? input.fallbackStart + index;
|
|
2396
|
+
const completedAt = turn.completedAt ?? startedAt;
|
|
2397
|
+
return {
|
|
2398
|
+
turnId: turn.id,
|
|
2399
|
+
snapshot: {
|
|
2400
|
+
sessionId: input.localSessionId,
|
|
2401
|
+
turnId: turn.id,
|
|
2402
|
+
prompt: turn.prompt ?? "未记录对话内容",
|
|
2403
|
+
status: "completed",
|
|
2404
|
+
startedAt,
|
|
2405
|
+
completedAt,
|
|
2406
|
+
assistantText: "",
|
|
2407
|
+
plan: [],
|
|
2408
|
+
activities: [],
|
|
2409
|
+
completedTools: [],
|
|
2410
|
+
failedTools: [],
|
|
2411
|
+
fileSummary: [],
|
|
2412
|
+
},
|
|
2413
|
+
updatedAt: new Date(completedAt).toISOString(),
|
|
2414
|
+
};
|
|
2415
|
+
}),
|
|
2416
|
+
});
|
|
2417
|
+
return input.completedTurns.length;
|
|
2418
|
+
}
|
|
2419
|
+
catch (error) {
|
|
2420
|
+
this.logger.warn({ error, remoteSessionId: input.remoteSessionId }, "Failed to persist inherited Turn history after Fork; keeping the Fork anchor fallback.");
|
|
2421
|
+
return 0;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
async hydrateLegacyForkTurnHistory(contextKey, current) {
|
|
2425
|
+
const source = this.store.getForkHistorySource(current.localSessionId);
|
|
2426
|
+
if (!source || source.sourceTurnHistoryCount !== undefined
|
|
2427
|
+
|| this.store.hasImportedForkTurnHistory(current.localSessionId))
|
|
2428
|
+
return;
|
|
2429
|
+
if (!source.sourceRemoteSessionId)
|
|
2430
|
+
return;
|
|
2431
|
+
const runtime = this.runtimes.forAgent(current.agentName);
|
|
2432
|
+
if (runtime.kind !== "codex" || !runtime.readRemoteSession)
|
|
2433
|
+
return;
|
|
2434
|
+
try {
|
|
2435
|
+
const remote = await runtime.readRemoteSession(source.sourceRemoteSessionId);
|
|
2436
|
+
const completedTurns = remote.completedTurns ?? [];
|
|
2437
|
+
const anchorIndex = completedTurns.findIndex((turn) => turn.id === source.sourceTurnId);
|
|
2438
|
+
if (anchorIndex < 0)
|
|
2439
|
+
return;
|
|
2440
|
+
const inheritedTurns = completedTurns.slice(0, anchorIndex + 1);
|
|
2441
|
+
const sourceSession = source.sourceLocalSessionId
|
|
2442
|
+
? this.store.getSession(source.sourceLocalSessionId)
|
|
2443
|
+
: undefined;
|
|
2444
|
+
const imported = this.persistCompletedTurnHistory({
|
|
2445
|
+
localSessionId: sourceSession?.localSessionId ?? current.localSessionId,
|
|
2446
|
+
contextKey: sourceSession?.contextKey ?? contextKey,
|
|
2447
|
+
agentName: current.agentName,
|
|
2448
|
+
remoteSessionId: source.sourceRemoteSessionId,
|
|
2449
|
+
completedTurns: inheritedTurns,
|
|
2450
|
+
fallbackStart: source.sourceTurnStartedAt
|
|
2451
|
+
?? source.sourceTurnCompletedAt
|
|
2452
|
+
?? Date.now() - inheritedTurns.length,
|
|
2453
|
+
});
|
|
2454
|
+
if (imported > 0) {
|
|
2455
|
+
this.store.audit(contextKey, "fork_turn_history_imported", {
|
|
2456
|
+
forkedLocalSessionId: current.localSessionId,
|
|
2457
|
+
sourceRemoteSessionId: source.sourceRemoteSessionId,
|
|
2458
|
+
sourceTurnId: source.sourceTurnId,
|
|
2459
|
+
importedTurnCount: imported,
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
catch (error) {
|
|
2464
|
+
this.logger.warn({ error, localSessionId: current.localSessionId, sourceRemoteSessionId: source.sourceRemoteSessionId }, "Failed to hydrate legacy Fork Turn history; rendering the locally available history.");
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2320
2467
|
async createProjectSessionFromReference(contextKey, reference) {
|
|
2321
2468
|
const resolved = await this.resolveProjectSessionReference(contextKey, reference);
|
|
2322
2469
|
const created = await this.createSession(contextKey, resolved.agentName, resolved.cwd, true, false, undefined, undefined, undefined, resolved.executionSettings);
|
|
@@ -2488,19 +2635,21 @@ export class ProxySessionController {
|
|
|
2488
2635
|
}
|
|
2489
2636
|
async createSession(contextKey, agentName, cwd, announce, prepareTurn, prompt, replyTarget, requestedTitle, executionSettings = {}) {
|
|
2490
2637
|
const agent = this.ensureAgent(agentName);
|
|
2638
|
+
const resolvedExecutionSettings = mergeExecutionSettings(agent.defaults ?? {}, executionSettings);
|
|
2491
2639
|
const localSessionId = createId("sess");
|
|
2492
2640
|
const initialTitle = normalizeTaskTitle(requestedTitle ?? prompt ?? "");
|
|
2493
2641
|
const sessionCwd = cwd === undefined && agent.kind === "app-server"
|
|
2494
2642
|
? createProjectlessWorkspace({ prompt: initialTitle }).cwd
|
|
2495
2643
|
: path.resolve(cwd ?? this.config.defaults.cwd);
|
|
2496
2644
|
const record = this.store.createSession({ localSessionId, contextKey, agentName, cwd: sessionCwd, status: "starting" });
|
|
2497
|
-
if (initialTitle ||
|
|
2645
|
+
if (initialTitle || resolvedExecutionSettings.modelProvider || resolvedExecutionSettings.model
|
|
2646
|
+
|| resolvedExecutionSettings.reasoningEffort || resolvedExecutionSettings.permissionMode) {
|
|
2498
2647
|
this.store.updateRuntimeSession(localSessionId, {
|
|
2499
2648
|
title: initialTitle,
|
|
2500
|
-
modelProvider:
|
|
2501
|
-
model:
|
|
2502
|
-
reasoningEffort:
|
|
2503
|
-
permissionMode:
|
|
2649
|
+
modelProvider: resolvedExecutionSettings.modelProvider,
|
|
2650
|
+
model: resolvedExecutionSettings.model,
|
|
2651
|
+
reasoningEffort: resolvedExecutionSettings.reasoningEffort,
|
|
2652
|
+
permissionMode: resolvedExecutionSettings.permissionMode,
|
|
2504
2653
|
});
|
|
2505
2654
|
}
|
|
2506
2655
|
this.store.setCurrentSession(contextKey, localSessionId);
|
|
@@ -2515,10 +2664,10 @@ export class ProxySessionController {
|
|
|
2515
2664
|
agentName,
|
|
2516
2665
|
cwd: sessionCwd,
|
|
2517
2666
|
title: initialTitle,
|
|
2518
|
-
modelProvider:
|
|
2519
|
-
model:
|
|
2520
|
-
reasoningEffort:
|
|
2521
|
-
permissionMode:
|
|
2667
|
+
modelProvider: resolvedExecutionSettings.modelProvider,
|
|
2668
|
+
model: resolvedExecutionSettings.model,
|
|
2669
|
+
reasoningEffort: resolvedExecutionSettings.reasoningEffort,
|
|
2670
|
+
permissionMode: resolvedExecutionSettings.permissionMode ?? "auto",
|
|
2522
2671
|
});
|
|
2523
2672
|
this.persistRuntimeSession(record, session, session.activeTurnId ? "running" : "ready");
|
|
2524
2673
|
const saved = this.store.getSession(localSessionId) ?? record;
|
|
@@ -3167,6 +3316,44 @@ export class ProxySessionController {
|
|
|
3167
3316
|
this.sessionResets.delete(sessionId);
|
|
3168
3317
|
}
|
|
3169
3318
|
}
|
|
3319
|
+
async resetFromHistoryCard(contextKey, sessionId, turnId, updateMessageId, page) {
|
|
3320
|
+
if (!sessionId || !turnId)
|
|
3321
|
+
throw new Error("无效的 Reset 请求。请使用最新的历史轮次卡片重试。");
|
|
3322
|
+
const existingTurnId = this.resetHistoryOperations.get(sessionId);
|
|
3323
|
+
if (existingTurnId) {
|
|
3324
|
+
await this.openResetHistory(contextKey, { expectedSessionId: sessionId, updateMessageId, page });
|
|
3325
|
+
throw new Error("当前已有 Reset 正在执行,请稍候。");
|
|
3326
|
+
}
|
|
3327
|
+
this.resetHistoryOperations.set(sessionId, turnId);
|
|
3328
|
+
try {
|
|
3329
|
+
await this.openResetHistory(contextKey, { expectedSessionId: sessionId, updateMessageId, page });
|
|
3330
|
+
}
|
|
3331
|
+
catch (error) {
|
|
3332
|
+
if (this.resetHistoryOperations.get(sessionId) === turnId) {
|
|
3333
|
+
this.resetHistoryOperations.delete(sessionId);
|
|
3334
|
+
}
|
|
3335
|
+
throw error;
|
|
3336
|
+
}
|
|
3337
|
+
try {
|
|
3338
|
+
await this.resetCurrentSessionToTurn(contextKey, sessionId, turnId);
|
|
3339
|
+
}
|
|
3340
|
+
catch (error) {
|
|
3341
|
+
if (this.resetHistoryOperations.get(sessionId) === turnId) {
|
|
3342
|
+
this.resetHistoryOperations.delete(sessionId);
|
|
3343
|
+
}
|
|
3344
|
+
try {
|
|
3345
|
+
await this.openResetHistory(contextKey, { expectedSessionId: sessionId, updateMessageId, page });
|
|
3346
|
+
}
|
|
3347
|
+
catch (refreshError) {
|
|
3348
|
+
this.logger.warn({ error: refreshError, contextKey, sessionId, turnId, updateMessageId }, "Failed to restore the Turns card after Reset failed.");
|
|
3349
|
+
}
|
|
3350
|
+
throw error;
|
|
3351
|
+
}
|
|
3352
|
+
if (this.resetHistoryOperations.get(sessionId) === turnId) {
|
|
3353
|
+
this.resetHistoryOperations.delete(sessionId);
|
|
3354
|
+
}
|
|
3355
|
+
await this.openResetHistory(contextKey, { expectedSessionId: sessionId, updateMessageId, page });
|
|
3356
|
+
}
|
|
3170
3357
|
async performCurrentSessionReset(contextKey, sessionId, turnId) {
|
|
3171
3358
|
const current = this.requireCurrentSession(contextKey);
|
|
3172
3359
|
if (current.localSessionId !== sessionId) {
|
|
@@ -3429,12 +3616,13 @@ export class ProxySessionController {
|
|
|
3429
3616
|
reasoningEffort: session.reasoningEffort ?? settings.effort,
|
|
3430
3617
|
permissionMode: session.permissionMode,
|
|
3431
3618
|
});
|
|
3619
|
+
await this.persistAgentExecutionDefaults(loaded.record.localSessionId);
|
|
3432
3620
|
const notice = [
|
|
3433
3621
|
`Provider 已切换为 ${cardCode(session.modelProvider ?? settings.provider)}`,
|
|
3434
3622
|
`模型 ${cardCode(session.model ?? settings.model)}`,
|
|
3435
3623
|
`思考强度 ${cardCode(session.reasoningEffort ?? settings.effort)}`,
|
|
3436
3624
|
`权限 ${cardCode(session.permissionMode)}`,
|
|
3437
|
-
|
|
3625
|
+
`并已保存为 ${cardCode(loaded.record.agentName)} Agent 的默认设置,从下一次请求生效。`,
|
|
3438
3626
|
].join(",");
|
|
3439
3627
|
if (options.updateMessageId) {
|
|
3440
3628
|
await this.openProviderSelector(contextKey, {
|
|
@@ -3497,6 +3685,7 @@ export class ProxySessionController {
|
|
|
3497
3685
|
await loaded.runtime.setReasoningEffort(loaded.record.localSessionId, nextEffort);
|
|
3498
3686
|
}
|
|
3499
3687
|
this.store.updateRuntimeSession(loaded.record.localSessionId, { model, reasoningEffort: nextEffort });
|
|
3688
|
+
await this.persistAgentExecutionDefaults(loaded.record.localSessionId);
|
|
3500
3689
|
const effortMessage = nextEffort && nextEffort !== currentEffort
|
|
3501
3690
|
? `,思考强度已自动调整为 ${nextEffort}`
|
|
3502
3691
|
: "";
|
|
@@ -3504,11 +3693,11 @@ export class ProxySessionController {
|
|
|
3504
3693
|
await this.openExecutionSettings(contextKey, "model", {
|
|
3505
3694
|
sessionId: loaded.record.localSessionId,
|
|
3506
3695
|
updateMessageId: options.updateMessageId,
|
|
3507
|
-
notice: `模型已切换为 ${cardCode(model)}${effortMessage}
|
|
3696
|
+
notice: `模型已切换为 ${cardCode(model)}${effortMessage},并已保存为 ${cardCode(loaded.record.agentName)} Agent 的默认设置,从下一次请求生效。`,
|
|
3508
3697
|
});
|
|
3509
3698
|
return;
|
|
3510
3699
|
}
|
|
3511
|
-
await this.outbound.sendText(contextKey, `模型已切换为 ${model}${effortMessage}
|
|
3700
|
+
await this.outbound.sendText(contextKey, `模型已切换为 ${model}${effortMessage},并已保存为 ${loaded.record.agentName} Agent 的默认设置,从下一次请求生效。`);
|
|
3512
3701
|
}
|
|
3513
3702
|
async goal(contextKey, command) {
|
|
3514
3703
|
const objective = command.action === "set" || command.action === "edit" ? command.objective : undefined;
|
|
@@ -3848,15 +4037,16 @@ export class ProxySessionController {
|
|
|
3848
4037
|
}
|
|
3849
4038
|
await loaded.runtime.setReasoningEffort(loaded.record.localSessionId, effort);
|
|
3850
4039
|
this.store.updateRuntimeSession(loaded.record.localSessionId, { reasoningEffort: effort });
|
|
4040
|
+
await this.persistAgentExecutionDefaults(loaded.record.localSessionId);
|
|
3851
4041
|
if (options.updateMessageId) {
|
|
3852
4042
|
await this.openExecutionSettings(contextKey, "thinking", {
|
|
3853
4043
|
sessionId: loaded.record.localSessionId,
|
|
3854
4044
|
updateMessageId: options.updateMessageId,
|
|
3855
|
-
notice: `思考强度已切换为 ${cardCode(effort)}
|
|
4045
|
+
notice: `思考强度已切换为 ${cardCode(effort)},并已保存为 ${cardCode(loaded.record.agentName)} Agent 的默认设置,从下一次请求生效。`,
|
|
3856
4046
|
});
|
|
3857
4047
|
return;
|
|
3858
4048
|
}
|
|
3859
|
-
await this.outbound.sendText(contextKey, `思考强度已切换为 ${effort}
|
|
4049
|
+
await this.outbound.sendText(contextKey, `思考强度已切换为 ${effort},并已保存为 ${loaded.record.agentName} Agent 的默认设置,从下一次请求生效。`);
|
|
3860
4050
|
}
|
|
3861
4051
|
async permissions(contextKey, mode, options = {}) {
|
|
3862
4052
|
if (!mode) {
|
|
@@ -3870,17 +4060,20 @@ export class ProxySessionController {
|
|
|
3870
4060
|
await loaded.runtime.setPermissionMode(record.localSessionId, mode);
|
|
3871
4061
|
loaded.session.permissionMode = mode;
|
|
3872
4062
|
this.store.updateRuntimeSession(record.localSessionId, { permissionMode: mode });
|
|
4063
|
+
await this.persistAgentExecutionDefaults(record.localSessionId);
|
|
3873
4064
|
if (options.updateMessageId) {
|
|
3874
4065
|
await this.openExecutionSettings(contextKey, "permission", {
|
|
3875
4066
|
sessionId: loaded.record.localSessionId,
|
|
3876
4067
|
updateMessageId: options.updateMessageId,
|
|
3877
4068
|
notice: mode === "auto"
|
|
3878
|
-
?
|
|
3879
|
-
:
|
|
4069
|
+
? `已切换为自动执行模式,并已保存为 ${cardCode(loaded.record.agentName)} Agent 的默认设置,从下一次请求生效。`
|
|
4070
|
+
: `已切换为执行前确认模式,并已保存为 ${cardCode(loaded.record.agentName)} Agent 的默认设置,从下一次请求生效。`,
|
|
3880
4071
|
});
|
|
3881
4072
|
return;
|
|
3882
4073
|
}
|
|
3883
|
-
await this.outbound.sendText(contextKey, mode === "auto"
|
|
4074
|
+
await this.outbound.sendText(contextKey, mode === "auto"
|
|
4075
|
+
? `已切换为自动执行模式,并已保存为 ${loaded.record.agentName} Agent 的默认设置。`
|
|
4076
|
+
: `已切换为执行前确认模式,并已保存为 ${loaded.record.agentName} Agent 的默认设置。`);
|
|
3884
4077
|
}
|
|
3885
4078
|
async openResetHistory(contextKey, options = {}) {
|
|
3886
4079
|
const current = this.requireCurrentSession(contextKey);
|
|
@@ -3890,6 +4083,7 @@ export class ProxySessionController {
|
|
|
3890
4083
|
if (!current.remoteSessionId || !this.isCodexSession(current)) {
|
|
3891
4084
|
throw new Error("当前任务不是可 Reset 的 App Server 任务。");
|
|
3892
4085
|
}
|
|
4086
|
+
await this.hydrateLegacyForkTurnHistory(contextKey, current);
|
|
3893
4087
|
const completedTurns = this.store.listTaskTurnGraph(current.localSessionId);
|
|
3894
4088
|
const completedTurnIds = new Set(completedTurns.map((turn) => turn.turnId));
|
|
3895
4089
|
const runningTurnId = current.lastTurnStatus === "running" && current.lastTurnId
|
|
@@ -3936,6 +4130,7 @@ export class ProxySessionController {
|
|
|
3936
4130
|
const page = Math.max(0, Math.min(Math.trunc(options.page ?? 0), totalPages - 1));
|
|
3937
4131
|
const offset = page * RESET_HISTORY_PAGE_SIZE;
|
|
3938
4132
|
const turns = allTurns.slice(offset, offset + RESET_HISTORY_PAGE_SIZE);
|
|
4133
|
+
const resettingTurnId = this.resetHistoryOperations.get(current.localSessionId);
|
|
3939
4134
|
const entries = turns.flatMap((turn, index) => {
|
|
3940
4135
|
const snapshot = turnViewSnapshot(turn.snapshot);
|
|
3941
4136
|
if (!snapshot)
|
|
@@ -3944,17 +4139,17 @@ export class ProxySessionController {
|
|
|
3944
4139
|
const summary = truncateText((snapshot.prompt ?? snapshot.taskTitle ?? "未记录对话内容").replace(/\s+/g, " ").trim(), 100);
|
|
3945
4140
|
const isCurrent = current.lastTurnId === turn.turnId && current.lastTurnStatus === "completed";
|
|
3946
4141
|
const isRunning = runningTurnId === turn.turnId;
|
|
4142
|
+
const isResetting = resettingTurnId === turn.turnId;
|
|
3947
4143
|
return [{
|
|
3948
4144
|
sequence: graph.sequence,
|
|
3949
4145
|
graphNodeLine: graph.nodeLine,
|
|
3950
4146
|
graphConnectorLine: graph.connectorLine,
|
|
3951
|
-
lines: [
|
|
3952
|
-
|
|
3953
|
-
`${formatResetTurnTime(snapshot.completedAt ?? snapshot.startedAt)} · ${cardText(turn.turnId)}`,
|
|
3954
|
-
],
|
|
4147
|
+
lines: [cardText(summary || "未记录对话内容")],
|
|
4148
|
+
timestamp: formatResetTurnTime(snapshot.completedAt ?? snapshot.startedAt),
|
|
3955
4149
|
current: isCurrent,
|
|
3956
4150
|
running: isRunning,
|
|
3957
|
-
|
|
4151
|
+
resetting: isResetting,
|
|
4152
|
+
actions: isCurrent || isRunning || resettingTurnId ? undefined : [{
|
|
3958
4153
|
text: "Reset",
|
|
3959
4154
|
value: {
|
|
3960
4155
|
action: "turn_reset",
|
|
@@ -3993,6 +4188,7 @@ export class ProxySessionController {
|
|
|
3993
4188
|
runningTurn
|
|
3994
4189
|
? `第 ${page + 1}/${totalPages} 页 · 共 ${total} 个 turn(${completedTurns.length} 个已完成,1 个运行中)`
|
|
3995
4190
|
: `第 ${page + 1}/${totalPages} 页 · 共 ${total} 个已完成 turn`,
|
|
4191
|
+
...(resettingTurnId ? ["正在 Reset 到所选轮次,请稍候…"] : []),
|
|
3996
4192
|
],
|
|
3997
4193
|
pageActions,
|
|
3998
4194
|
});
|
|
@@ -4995,6 +5191,21 @@ export class ProxySessionController {
|
|
|
4995
5191
|
throw new Error(`未知 agent:${agentName}`);
|
|
4996
5192
|
return agent;
|
|
4997
5193
|
}
|
|
5194
|
+
async persistAgentExecutionDefaults(localSessionId) {
|
|
5195
|
+
const session = this.store.getSession(localSessionId);
|
|
5196
|
+
if (!session)
|
|
5197
|
+
throw new Error(`找不到任务:${localSessionId}`);
|
|
5198
|
+
const agent = this.ensureAgent(session.agentName);
|
|
5199
|
+
const observed = {
|
|
5200
|
+
...(session.modelProvider?.trim() ? { modelProvider: session.modelProvider } : {}),
|
|
5201
|
+
...(session.model?.trim() ? { model: session.model } : {}),
|
|
5202
|
+
...(session.reasoningEffort?.trim() ? { reasoningEffort: session.reasoningEffort } : {}),
|
|
5203
|
+
...(session.permissionMode ? { permissionMode: session.permissionMode } : {}),
|
|
5204
|
+
};
|
|
5205
|
+
const defaults = { ...(agent.defaults ?? {}), ...observed };
|
|
5206
|
+
await this.lifecycle?.persistAgentExecutionDefaults?.(session.agentName, defaults);
|
|
5207
|
+
agent.defaults = defaults;
|
|
5208
|
+
}
|
|
4998
5209
|
agentLabel(agentName) {
|
|
4999
5210
|
return this.ensureAgent(agentName).title;
|
|
5000
5211
|
}
|
|
@@ -5056,6 +5267,14 @@ function threadForkAnchorMessageIds(message) {
|
|
|
5056
5267
|
return [...new Set([message.rootMessageId, message.parentMessageId]
|
|
5057
5268
|
.filter((messageId) => Boolean(messageId && messageId !== message.messageId)))];
|
|
5058
5269
|
}
|
|
5270
|
+
function mergeExecutionSettings(defaults, overrides) {
|
|
5271
|
+
return {
|
|
5272
|
+
modelProvider: overrides.modelProvider ?? defaults.modelProvider,
|
|
5273
|
+
model: overrides.model ?? defaults.model,
|
|
5274
|
+
reasoningEffort: overrides.reasoningEffort ?? defaults.reasoningEffort,
|
|
5275
|
+
permissionMode: overrides.permissionMode ?? defaults.permissionMode,
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5059
5278
|
function sessionStatusLabel(status, activeTurnId) {
|
|
5060
5279
|
if (activeTurnId || status === "running")
|
|
5061
5280
|
return "执行中";
|
|
@@ -5490,6 +5709,12 @@ function mergeRemoteTaskStatus(record, remote) {
|
|
|
5490
5709
|
function normalizeRemoteTimestamp(value) {
|
|
5491
5710
|
return value >= 10_000_000_000 ? value : value * 1_000;
|
|
5492
5711
|
}
|
|
5712
|
+
function parseIsoTimestamp(value) {
|
|
5713
|
+
if (!value)
|
|
5714
|
+
return undefined;
|
|
5715
|
+
const timestamp = Date.parse(value);
|
|
5716
|
+
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
5717
|
+
}
|
|
5493
5718
|
function latestRemoteTimestamp(...values) {
|
|
5494
5719
|
const timestamps = values
|
|
5495
5720
|
.filter((value) => value !== undefined)
|