@keyou007/agent-bot 0.1.20 → 0.1.21
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 +10 -0
- package/README.md +15 -5
- package/README.zh.md +15 -5
- package/dist/codex/CodexEventMapper.js +21 -1
- package/dist/codex/CodexEventMapper.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +3 -0
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +136 -4
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/codex/ProviderModels.js +159 -0
- package/dist/codex/ProviderModels.js.map +1 -0
- package/dist/commands/CommandRouter.js +8 -3
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/console/ConsoleTurnPresenter.js +2 -1
- package/dist/console/ConsoleTurnPresenter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +46 -16
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +34 -8
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/local-files/LocalFileViewerServer.js +151 -16
- package/dist/local-files/LocalFileViewerServer.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +725 -0
- package/dist/local-files/TurnPreviewPage.js.map +1 -0
- package/dist/presentation/OutboundRouter.js +6 -1
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +142 -3
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +186 -80
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +7 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/utils/formatStorageSize.js +15 -0
- package/dist/utils/formatStorageSize.js.map +1 -0
- package/docs/technical-reference.md +7 -1
- package/docs/technical-reference.zh.md +7 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +5 -1
- package/src/codex/CodexEventMapper.ts +31 -1
- package/src/codex/CodexProcessManager.ts +4 -0
- package/src/codex/CodexRuntime.ts +153 -9
- package/src/codex/ProviderModels.ts +185 -0
- package/src/commands/CommandRouter.ts +7 -2
- package/src/commands/commandTypes.ts +1 -1
- package/src/console/ConsoleTurnPresenter.ts +3 -0
- package/src/feishu/CardRenderer.ts +47 -16
- package/src/feishu/FeishuTurnPresenter.ts +46 -4
- package/src/index.ts +2 -0
- package/src/local-files/LocalFileViewerServer.ts +166 -17
- package/src/local-files/TurnPreviewPage.ts +792 -0
- package/src/presentation/OutboundRouter.ts +8 -0
- package/src/presentation/TurnStateReducer.ts +175 -2
- package/src/presentation/turnViewTypes.ts +13 -0
- package/src/proxy/ProxySessionController.ts +186 -67
- package/src/runtime/types.ts +18 -1
- package/src/state/StateStore.ts +9 -0
- package/src/utils/formatStorageSize.ts +13 -0
|
@@ -21,6 +21,7 @@ import { errorLogValue } from "../logging/errorLogValue.js";
|
|
|
21
21
|
import { buildTurnGraphRows } from "../presentation/turnGraph.js";
|
|
22
22
|
import { isActiveShellCommandJob, ShellCommandJobManager, } from "../shell/ShellCommandJobManager.js";
|
|
23
23
|
import { createId } from "../utils/id.js";
|
|
24
|
+
import { formatStorageSize } from "../utils/formatStorageSize.js";
|
|
24
25
|
import { truncateMiddle, truncateText } from "../utils/markdown.js";
|
|
25
26
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
26
27
|
import { executeShellCommand, } from "../utils/executeShellCommand.js";
|
|
@@ -108,7 +109,7 @@ const HELP_COMMAND_SECTIONS = [
|
|
|
108
109
|
usage: "[序号或任务 ID]",
|
|
109
110
|
description: "从当前或指定任务最近完成轮次创建分支",
|
|
110
111
|
},
|
|
111
|
-
{ command: "/turns", description: "浏览历史轮次,并可 Reset
|
|
112
|
+
{ command: "/turns", usage: "[Turn ID 或序号]", description: "浏览历史轮次,并可 Reset 对话上下文;带参数查看运行详情" },
|
|
112
113
|
{
|
|
113
114
|
command: "/title",
|
|
114
115
|
usage: "<新标题>",
|
|
@@ -1031,7 +1032,7 @@ export class ProxySessionController {
|
|
|
1031
1032
|
?? { ...record, contextKey: responseContextKey };
|
|
1032
1033
|
const previous = this.messageQueues.get(responseContextKey) ?? Promise.resolve();
|
|
1033
1034
|
const next = previous.catch(() => undefined).then(async () => {
|
|
1034
|
-
this.outbound.registerSession(scopedRecord.localSessionId, responseContextKey, scopedRecord.title, scopedRecord.cwd, this.agentLabel(scopedRecord.agentName));
|
|
1035
|
+
this.outbound.registerSession(scopedRecord.localSessionId, responseContextKey, scopedRecord.title, scopedRecord.cwd, this.agentLabel(scopedRecord.agentName), scopedRecord.model);
|
|
1035
1036
|
const promptMessageId = await this.outbound.withReplyTarget(responseContextKey, existingReplyTarget, () => this.outbound.sendText(responseContextKey, promptText));
|
|
1036
1037
|
const turnReplyTarget = isThreadContextKey(responseContextKey)
|
|
1037
1038
|
? promptMessageId
|
|
@@ -1200,16 +1201,11 @@ export class ProxySessionController {
|
|
|
1200
1201
|
const loaded = await this.loadSession(record);
|
|
1201
1202
|
if (setting === "provider" && nextValue) {
|
|
1202
1203
|
await this.assertModelProvider(loaded, nextValue);
|
|
1203
|
-
const models = await loaded.runtime.listModels();
|
|
1204
|
-
const model = models
|
|
1205
|
-
?? models.find((candidate) => candidate.isDefault);
|
|
1204
|
+
const models = await loaded.runtime.listModels(nextValue, loaded.session.model);
|
|
1205
|
+
const model = selectProviderModel(models, loaded.session.model);
|
|
1206
1206
|
if (!model)
|
|
1207
1207
|
throw new Error("The current runtime has no model available for a Provider change.");
|
|
1208
|
-
const effort = model
|
|
1209
|
-
? loaded.session.reasoningEffort
|
|
1210
|
-
: model.defaultReasoningEffort ?? model.supportedReasoningEfforts[0]?.value;
|
|
1211
|
-
if (!effort)
|
|
1212
|
-
throw new Error(`Model ${model.id} has no reasoning effort available for a Provider change.`);
|
|
1208
|
+
const effort = selectProviderReasoningEffort(model, loaded.session.reasoningEffort);
|
|
1213
1209
|
await this.changeProviderSettings(loaded, {
|
|
1214
1210
|
modelProvider: nextValue,
|
|
1215
1211
|
model: model.id,
|
|
@@ -1218,12 +1214,13 @@ export class ProxySessionController {
|
|
|
1218
1214
|
});
|
|
1219
1215
|
}
|
|
1220
1216
|
else if (setting === "model" && nextValue) {
|
|
1221
|
-
const models = await loaded.runtime.listModels();
|
|
1217
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
1222
1218
|
const selected = models.find((candidate) => candidate.id === nextValue);
|
|
1223
1219
|
if (!selected)
|
|
1224
1220
|
throw new Error(`Unknown model: ${nextValue}`);
|
|
1225
1221
|
const currentEffort = loaded.session.reasoningEffort;
|
|
1226
|
-
const nextEffort = currentEffort && selected.supportedReasoningEfforts.
|
|
1222
|
+
const nextEffort = currentEffort && (selected.supportedReasoningEfforts.length === 0
|
|
1223
|
+
|| selected.supportedReasoningEfforts.some((candidate) => candidate.value === currentEffort))
|
|
1227
1224
|
? currentEffort
|
|
1228
1225
|
: selected.defaultReasoningEffort;
|
|
1229
1226
|
await loaded.runtime.setModel(record.localSessionId, nextValue);
|
|
@@ -1233,7 +1230,7 @@ export class ProxySessionController {
|
|
|
1233
1230
|
this.store.updateRuntimeSession(record.localSessionId, { model: nextValue, reasoningEffort: nextEffort });
|
|
1234
1231
|
}
|
|
1235
1232
|
else if (setting === "thinking" && nextValue) {
|
|
1236
|
-
const models = await loaded.runtime.listModels();
|
|
1233
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
1237
1234
|
const currentModel = models.find((candidate) => candidate.id === loaded.session.model)
|
|
1238
1235
|
?? models.find((candidate) => candidate.isDefault);
|
|
1239
1236
|
if (!currentModel)
|
|
@@ -1255,7 +1252,7 @@ export class ProxySessionController {
|
|
|
1255
1252
|
const session = this.store.getSession(record.localSessionId) ?? record;
|
|
1256
1253
|
if (setting && setting !== "provider")
|
|
1257
1254
|
await this.persistAgentExecutionDefaults(session.localSessionId);
|
|
1258
|
-
const models = await loaded.runtime.listModels();
|
|
1255
|
+
const models = await loaded.runtime.listModels(session.modelProvider, session.model);
|
|
1259
1256
|
const currentModel = models.find((candidate) => candidate.id === session.model)
|
|
1260
1257
|
?? models.find((candidate) => candidate.isDefault);
|
|
1261
1258
|
const providers = loaded.runtime.listModelProviders
|
|
@@ -1542,8 +1539,14 @@ export class ProxySessionController {
|
|
|
1542
1539
|
await this.setGroupMute(contextKey, command.enabled);
|
|
1543
1540
|
return;
|
|
1544
1541
|
case "turns":
|
|
1545
|
-
|
|
1546
|
-
|
|
1542
|
+
if (command.turnReference) {
|
|
1543
|
+
await this.outbound.sendText(contextKey, "正在读取轮次运行信息,请稍后。");
|
|
1544
|
+
await this.openTurnDetails(contextKey, command.turnReference);
|
|
1545
|
+
}
|
|
1546
|
+
else {
|
|
1547
|
+
await this.outbound.sendText(contextKey, LIST_TURNS_STARTED_MESSAGE);
|
|
1548
|
+
await this.openResetHistory(contextKey);
|
|
1549
|
+
}
|
|
1547
1550
|
return;
|
|
1548
1551
|
case "model":
|
|
1549
1552
|
await this.openExecutionSettings(contextKey, "model");
|
|
@@ -1577,7 +1580,7 @@ export class ProxySessionController {
|
|
|
1577
1580
|
: context.contextKey;
|
|
1578
1581
|
if (!this.outbound.canRoute(turnContextKey))
|
|
1579
1582
|
continue;
|
|
1580
|
-
this.outbound.registerSession(session.localSessionId, turnContextKey, session.title, session.cwd, this.agentLabel(session.agentName));
|
|
1583
|
+
this.outbound.registerSession(session.localSessionId, turnContextKey, session.title, session.cwd, this.agentLabel(session.agentName), session.model);
|
|
1581
1584
|
if (!session.lastTurnId)
|
|
1582
1585
|
continue;
|
|
1583
1586
|
void this.outbound.resumeDelivery(session.localSessionId, turnContextKey, session.lastTurnId).catch((error) => {
|
|
@@ -1651,7 +1654,7 @@ export class ProxySessionController {
|
|
|
1651
1654
|
continue;
|
|
1652
1655
|
}
|
|
1653
1656
|
if (session) {
|
|
1654
|
-
this.outbound.registerSession(session.localSessionId, attempt.contextKey, session.title, session.cwd, this.agentLabel(session.agentName));
|
|
1657
|
+
this.outbound.registerSession(session.localSessionId, attempt.contextKey, session.title, session.cwd, this.agentLabel(session.agentName), session.model);
|
|
1655
1658
|
}
|
|
1656
1659
|
try {
|
|
1657
1660
|
await this.handleRuntimeEvent({
|
|
@@ -1763,7 +1766,7 @@ export class ProxySessionController {
|
|
|
1763
1766
|
const replyTarget = attempt.replyMessageId
|
|
1764
1767
|
? { messageId: attempt.replyMessageId, replyInThread: true }
|
|
1765
1768
|
: undefined;
|
|
1766
|
-
this.outbound.registerSession(session.localSessionId, contextKey, session.title, session.cwd, this.agentLabel(session.agentName));
|
|
1769
|
+
this.outbound.registerSession(session.localSessionId, contextKey, session.title, session.cwd, this.agentLabel(session.agentName), session.model);
|
|
1767
1770
|
if (announce) {
|
|
1768
1771
|
await this.outbound.withReplyTarget(contextKey, replyTarget, () => this.outbound.sendText(contextKey, "检测到任务在 Agent Bot 重启前尚未完成,正在自动恢复。")).catch((error) => {
|
|
1769
1772
|
this.logger.warn({ error, attemptId: attempt.attemptId, contextKey }, "Failed to send the task recovery notification.");
|
|
@@ -1965,7 +1968,7 @@ export class ProxySessionController {
|
|
|
1965
1968
|
}
|
|
1966
1969
|
const configuredRuntime = this.runtimes.forAgent(record.agentName);
|
|
1967
1970
|
if (!configuredRuntime.getSession(record.localSessionId)) {
|
|
1968
|
-
this.outbound.registerSession(record.localSessionId, contextKey, record.title, record.cwd, this.agentLabel(record.agentName));
|
|
1971
|
+
this.outbound.registerSession(record.localSessionId, contextKey, record.title, record.cwd, this.agentLabel(record.agentName), record.model);
|
|
1969
1972
|
await this.outbound.startPendingTurn(record.localSessionId, contextKey, record.title, replyTarget, displayPrompt ?? text);
|
|
1970
1973
|
}
|
|
1971
1974
|
let loaded;
|
|
@@ -2258,7 +2261,7 @@ export class ProxySessionController {
|
|
|
2258
2261
|
permissionMode: source.permissionMode ?? "auto",
|
|
2259
2262
|
});
|
|
2260
2263
|
this.store.setCurrentSession(message.contextKey, localSessionId);
|
|
2261
|
-
this.outbound.registerSession(localSessionId, message.contextKey, forkTitle, source.cwd, this.agentLabel(source.agentName));
|
|
2264
|
+
this.outbound.registerSession(localSessionId, message.contextKey, forkTitle, source.cwd, this.agentLabel(source.agentName), source.model);
|
|
2262
2265
|
try {
|
|
2263
2266
|
const forked = await runtime.forkSession({
|
|
2264
2267
|
localSessionId,
|
|
@@ -2555,7 +2558,7 @@ export class ProxySessionController {
|
|
|
2555
2558
|
lastTurnId: plan.lastTurnId,
|
|
2556
2559
|
lastTurnStatus: plan.lastTurnStatus,
|
|
2557
2560
|
});
|
|
2558
|
-
this.outbound.registerSession(localSessionId, contextKey, forked.title ?? plan.forkTitle, plan.cwd, this.agentLabel(plan.agentName));
|
|
2561
|
+
this.outbound.registerSession(localSessionId, contextKey, forked.title ?? plan.forkTitle, plan.cwd, this.agentLabel(plan.agentName), plan.model);
|
|
2559
2562
|
this.store.audit(contextKey, "session_forked", {
|
|
2560
2563
|
sourceLocalSessionId: plan.source?.localSessionId,
|
|
2561
2564
|
sourceRemoteSessionId: plan.remoteSessionId,
|
|
@@ -2616,15 +2619,15 @@ export class ProxySessionController {
|
|
|
2616
2619
|
return 0;
|
|
2617
2620
|
}
|
|
2618
2621
|
}
|
|
2619
|
-
async hydrateTaskTurnHistory(current, requiredCount = RESET_HISTORY_PAGE_SIZE) {
|
|
2622
|
+
async hydrateTaskTurnHistory(current, requiredCount = RESET_HISTORY_PAGE_SIZE, targetTurnId) {
|
|
2620
2623
|
const existing = this.turnHistoryHydrations.get(current.localSessionId);
|
|
2621
2624
|
if (existing)
|
|
2622
2625
|
await existing;
|
|
2623
2626
|
const hydration = (async () => {
|
|
2624
2627
|
if (this.store.getForkHistorySource(current.localSessionId)) {
|
|
2625
|
-
return this.hydrateForkTurnHistory(current.contextKey, current, requiredCount);
|
|
2628
|
+
return this.hydrateForkTurnHistory(current.contextKey, current, requiredCount, new Set(), targetTurnId);
|
|
2626
2629
|
}
|
|
2627
|
-
return this.hydrateRemoteTurnHistory(current.contextKey, current, requiredCount);
|
|
2630
|
+
return this.hydrateRemoteTurnHistory(current.contextKey, current, requiredCount, targetTurnId);
|
|
2628
2631
|
})();
|
|
2629
2632
|
this.turnHistoryHydrations.set(current.localSessionId, hydration);
|
|
2630
2633
|
try {
|
|
@@ -2636,7 +2639,7 @@ export class ProxySessionController {
|
|
|
2636
2639
|
}
|
|
2637
2640
|
}
|
|
2638
2641
|
}
|
|
2639
|
-
async hydrateForkTurnHistory(contextKey, current, requiredCount, visited = new Set()) {
|
|
2642
|
+
async hydrateForkTurnHistory(contextKey, current, requiredCount, visited = new Set(), targetTurnId) {
|
|
2640
2643
|
if (visited.has(current.localSessionId))
|
|
2641
2644
|
return false;
|
|
2642
2645
|
visited.add(current.localSessionId);
|
|
@@ -2667,7 +2670,7 @@ export class ProxySessionController {
|
|
|
2667
2670
|
? Math.max(0, localAnchorIndex)
|
|
2668
2671
|
: localAnchorIndex >= 0 ? localAnchorIndex : availableCount;
|
|
2669
2672
|
const neededInheritedCount = Math.max(0, requiredCount - ownTurnCount);
|
|
2670
|
-
if (localAnchorIndex >= 0 && availableCount >= requiredCount
|
|
2673
|
+
if (!targetTurnId && localAnchorIndex >= 0 && availableCount >= requiredCount
|
|
2671
2674
|
&& (!state.anchorFound || (state.loadedCount ?? 0) >= neededInheritedCount))
|
|
2672
2675
|
return !state.complete;
|
|
2673
2676
|
const sourceSession = source.sourceLocalSessionId
|
|
@@ -2677,7 +2680,7 @@ export class ProxySessionController {
|
|
|
2677
2680
|
// the Agent materializes that intermediate branch.
|
|
2678
2681
|
if (sourceSession && sourceSession.localSessionId !== current.localSessionId
|
|
2679
2682
|
&& this.store.getForkHistorySource(sourceSession.localSessionId)?.sourceTurnId === source.sourceTurnId) {
|
|
2680
|
-
const hasMore = await this.hydrateForkTurnHistory(sourceSession.contextKey, sourceSession, this.store.listTaskTurnGraphIndex(sourceSession.localSessionId).length + requiredCount - availableCount, visited);
|
|
2683
|
+
const hasMore = await this.hydrateForkTurnHistory(sourceSession.contextKey, sourceSession, this.store.listTaskTurnGraphIndex(sourceSession.localSessionId).length + requiredCount - availableCount, visited, targetTurnId);
|
|
2681
2684
|
if (!hasMore && this.store.hasImportedForkTurnHistory(sourceSession.localSessionId)) {
|
|
2682
2685
|
this.store.audit(contextKey, "fork_turn_history_imported", {
|
|
2683
2686
|
forkedLocalSessionId: current.localSessionId,
|
|
@@ -2695,8 +2698,10 @@ export class ProxySessionController {
|
|
|
2695
2698
|
return false;
|
|
2696
2699
|
const seenCursors = new Set();
|
|
2697
2700
|
try {
|
|
2698
|
-
while (!state.complete && (
|
|
2699
|
-
|
|
2701
|
+
while (!state.complete && (targetTurnId
|
|
2702
|
+
? !this.store.hasTaskHistoryTurn(current.localSessionId, targetTurnId)
|
|
2703
|
+
: (state.loadedCount ?? 0) < neededInheritedCount)) {
|
|
2704
|
+
const remaining = targetTurnId ? RESET_HISTORY_PAGE_SIZE : neededInheritedCount - (state.loadedCount ?? 0);
|
|
2700
2705
|
const page = await runtime.listRemoteTurnSummaries(source.sourceRemoteSessionId, {
|
|
2701
2706
|
cursor: state.cursor, limit: Math.min(RESET_HISTORY_PAGE_SIZE, remaining),
|
|
2702
2707
|
});
|
|
@@ -2756,7 +2761,7 @@ export class ProxySessionController {
|
|
|
2756
2761
|
}
|
|
2757
2762
|
return true;
|
|
2758
2763
|
}
|
|
2759
|
-
async hydrateRemoteTurnHistory(contextKey, current, requiredCount) {
|
|
2764
|
+
async hydrateRemoteTurnHistory(contextKey, current, requiredCount, targetTurnId) {
|
|
2760
2765
|
if (!current.remoteSessionId)
|
|
2761
2766
|
return false;
|
|
2762
2767
|
const key = JSON.stringify([current.agentName, current.remoteSessionId, "turn-card"]);
|
|
@@ -2770,10 +2775,12 @@ export class ProxySessionController {
|
|
|
2770
2775
|
return false;
|
|
2771
2776
|
const seen = new Set();
|
|
2772
2777
|
try {
|
|
2773
|
-
while (!state.complete && (
|
|
2778
|
+
while (!state.complete && (targetTurnId
|
|
2779
|
+
? !this.store.hasTaskHistoryTurn(current.localSessionId, targetTurnId)
|
|
2780
|
+
: (state.loadedCount ?? 0) < requiredCount)) {
|
|
2774
2781
|
const page = await runtime.listRemoteTurnSummaries(current.remoteSessionId, {
|
|
2775
2782
|
cursor: state.cursor,
|
|
2776
|
-
limit: Math.min(RESET_HISTORY_PAGE_SIZE, requiredCount - (state.loadedCount ?? 0)),
|
|
2783
|
+
limit: targetTurnId ? RESET_HISTORY_PAGE_SIZE : Math.min(RESET_HISTORY_PAGE_SIZE, requiredCount - (state.loadedCount ?? 0)),
|
|
2777
2784
|
});
|
|
2778
2785
|
if (page.nextCursor && (page.nextCursor === state.cursor || seen.has(page.nextCursor))) {
|
|
2779
2786
|
throw new Error("App Server repeated a Turn history cursor.");
|
|
@@ -2998,7 +3005,7 @@ export class ProxySessionController {
|
|
|
2998
3005
|
});
|
|
2999
3006
|
}
|
|
3000
3007
|
this.store.setCurrentSession(contextKey, localSessionId);
|
|
3001
|
-
this.outbound.registerSession(localSessionId, contextKey, initialTitle, sessionCwd, this.agentLabel(agentName));
|
|
3008
|
+
this.outbound.registerSession(localSessionId, contextKey, initialTitle, sessionCwd, this.agentLabel(agentName), resolvedExecutionSettings.model);
|
|
3002
3009
|
const runtime = this.runtimes.forAgent(agentName);
|
|
3003
3010
|
try {
|
|
3004
3011
|
if (prepareTurn) {
|
|
@@ -3015,6 +3022,7 @@ export class ProxySessionController {
|
|
|
3015
3022
|
permissionMode: resolvedExecutionSettings.permissionMode ?? "auto",
|
|
3016
3023
|
});
|
|
3017
3024
|
this.persistRuntimeSession(record, session, session.activeTurnId ? "running" : "ready");
|
|
3025
|
+
this.outbound.updateSessionModel(localSessionId, session.model);
|
|
3018
3026
|
const saved = this.store.getSession(localSessionId) ?? record;
|
|
3019
3027
|
if (announce) {
|
|
3020
3028
|
const task = initialTitle ? `${initialTitle}(${session.remoteSessionId})` : session.remoteSessionId;
|
|
@@ -3162,7 +3170,7 @@ export class ProxySessionController {
|
|
|
3162
3170
|
record: { ...loaded.record, contextKey: record.contextKey },
|
|
3163
3171
|
};
|
|
3164
3172
|
}
|
|
3165
|
-
this.outbound.registerSession(record.localSessionId, record.contextKey, record.title, record.cwd, this.agentLabel(record.agentName));
|
|
3173
|
+
this.outbound.registerSession(record.localSessionId, record.contextKey, record.title, record.cwd, this.agentLabel(record.agentName), record.model);
|
|
3166
3174
|
const loading = (async () => {
|
|
3167
3175
|
if (record.lastTurnId) {
|
|
3168
3176
|
try {
|
|
@@ -3303,14 +3311,20 @@ export class ProxySessionController {
|
|
|
3303
3311
|
lastTurnStatus: event.type === "turn_completed" ? "completed" : event.type === "turn_cancelled" ? "cancelled" : "failed",
|
|
3304
3312
|
});
|
|
3305
3313
|
}
|
|
3314
|
+
const enrichedEvent = event.type === "context_compaction"
|
|
3315
|
+
? {
|
|
3316
|
+
...event,
|
|
3317
|
+
turnCount: event.turnCount ?? this.store.countTaskTurns(event.sessionId),
|
|
3318
|
+
}
|
|
3319
|
+
: event;
|
|
3306
3320
|
const presentationEvent = event.type === "turn_failed"
|
|
3307
3321
|
&& isRetryableLlmTurnFailure(event.message)
|
|
3308
3322
|
&& (failedAttempt?.retryCount ?? 0) >= MAX_LLM_TURN_RETRIES
|
|
3309
3323
|
? {
|
|
3310
|
-
...
|
|
3324
|
+
...enrichedEvent,
|
|
3311
3325
|
message: `${event.message}\n\n已自动重试 ${MAX_LLM_TURN_RETRIES} 次,仍未成功。`,
|
|
3312
3326
|
}
|
|
3313
|
-
:
|
|
3327
|
+
: enrichedEvent;
|
|
3314
3328
|
let presentationError;
|
|
3315
3329
|
try {
|
|
3316
3330
|
await this.outbound.onEvent(presentationEvent);
|
|
@@ -3807,7 +3821,7 @@ export class ProxySessionController {
|
|
|
3807
3821
|
lastTurnId: turnId,
|
|
3808
3822
|
lastTurnStatus: "completed",
|
|
3809
3823
|
});
|
|
3810
|
-
this.outbound.registerSession(current.localSessionId, contextKey, forked.title ?? current.title, current.cwd, this.agentLabel(current.agentName));
|
|
3824
|
+
this.outbound.registerSession(current.localSessionId, contextKey, forked.title ?? current.title, current.cwd, this.agentLabel(current.agentName), current.model);
|
|
3811
3825
|
this.store.audit(contextKey, "session_reset_to_turn", {
|
|
3812
3826
|
localSessionId: current.localSessionId,
|
|
3813
3827
|
previousRemoteSessionId,
|
|
@@ -3857,12 +3871,13 @@ export class ProxySessionController {
|
|
|
3857
3871
|
};
|
|
3858
3872
|
if (record) {
|
|
3859
3873
|
const loaded = await this.loadSession(record);
|
|
3860
|
-
const models = await loaded.runtime.listModels();
|
|
3861
|
-
const currentModel = models.find((model) => model.id === loaded.session.model)
|
|
3862
|
-
?? models.find((model) => model.isDefault);
|
|
3863
3874
|
const providerSupported = loaded.runtime.kind === "codex" && Boolean(loaded.runtime.listModelProviders);
|
|
3864
3875
|
const providers = providerSupported ? await this.modelProviderOptions(loaded) : [];
|
|
3865
3876
|
const currentProvider = loaded.session.modelProvider ?? providers.find((provider) => provider.isDefault)?.id;
|
|
3877
|
+
const models = await loaded.runtime.listModels(currentProvider, loaded.session.model);
|
|
3878
|
+
const currentModel = models.find((model) => model.id === loaded.session.model)
|
|
3879
|
+
?? models.find((model) => model.isDefault)
|
|
3880
|
+
?? models[0];
|
|
3866
3881
|
const currentEffort = currentModel?.supportedReasoningEfforts.some((option) => option.value === loaded.session.reasoningEffort)
|
|
3867
3882
|
? loaded.session.reasoningEffort
|
|
3868
3883
|
: currentModel?.defaultReasoningEffort ?? loaded.session.reasoningEffort;
|
|
@@ -3923,16 +3938,11 @@ export class ProxySessionController {
|
|
|
3923
3938
|
await this.outbound.sendText(contextKey, `正在切换到 Provider ${modelProvider},请稍后。`);
|
|
3924
3939
|
const loaded = await this.loadSession(record);
|
|
3925
3940
|
await this.assertModelProvider(loaded, modelProvider);
|
|
3926
|
-
const models = await loaded.runtime.listModels();
|
|
3927
|
-
const model = models
|
|
3928
|
-
?? models.find((candidate) => candidate.isDefault);
|
|
3941
|
+
const models = await loaded.runtime.listModels(modelProvider, loaded.session.model);
|
|
3942
|
+
const model = selectProviderModel(models, loaded.session.model);
|
|
3929
3943
|
if (!model)
|
|
3930
3944
|
throw new Error("当前运行时没有可用于 Provider 切换的模型。");
|
|
3931
|
-
const effort = model
|
|
3932
|
-
? loaded.session.reasoningEffort
|
|
3933
|
-
: model.defaultReasoningEffort ?? model.supportedReasoningEfforts[0]?.value;
|
|
3934
|
-
if (!effort)
|
|
3935
|
-
throw new Error(`模型 ${model.id} 没有可用于 Provider 切换的思考强度。`);
|
|
3945
|
+
const effort = selectProviderReasoningEffort(model, loaded.session.reasoningEffort);
|
|
3936
3946
|
await this.applyProviderSettings(contextKey, {
|
|
3937
3947
|
provider: modelProvider,
|
|
3938
3948
|
model: model.id,
|
|
@@ -3946,11 +3956,11 @@ export class ProxySessionController {
|
|
|
3946
3956
|
async openProviderModelSelector(contextKey, modelProvider, permissionMode, sessionId, updateMessageId) {
|
|
3947
3957
|
const loaded = await this.loadSession(this.requireSession(contextKey, sessionId));
|
|
3948
3958
|
await this.assertModelProvider(loaded, modelProvider);
|
|
3949
|
-
const models = await loaded.runtime.listModels();
|
|
3959
|
+
const models = await loaded.runtime.listModels(modelProvider, loaded.session.model);
|
|
3950
3960
|
const card = this.cardRenderer.renderModelSelector({
|
|
3951
3961
|
sessionId,
|
|
3952
3962
|
contextKey,
|
|
3953
|
-
currentModel: loaded.session.model
|
|
3963
|
+
currentModel: selectProviderModel(models, loaded.session.model)?.id,
|
|
3954
3964
|
reasoningEffort: loaded.session.reasoningEffort,
|
|
3955
3965
|
models,
|
|
3956
3966
|
modelProvider,
|
|
@@ -3962,20 +3972,23 @@ export class ProxySessionController {
|
|
|
3962
3972
|
async openProviderReasoningSelector(contextKey, modelProvider, model, permissionMode, sessionId, updateMessageId) {
|
|
3963
3973
|
const loaded = await this.loadSession(this.requireSession(contextKey, sessionId));
|
|
3964
3974
|
await this.assertModelProvider(loaded, modelProvider);
|
|
3965
|
-
const models = await loaded.runtime.listModels();
|
|
3975
|
+
const models = await loaded.runtime.listModels(modelProvider, model);
|
|
3966
3976
|
const selected = models.find((candidate) => candidate.id === model);
|
|
3967
3977
|
if (!selected)
|
|
3968
3978
|
throw new Error(`未知模型:${model}`);
|
|
3969
|
-
const
|
|
3979
|
+
const options = selected.supportedReasoningEfforts.length > 0
|
|
3980
|
+
? selected.supportedReasoningEfforts
|
|
3981
|
+
: [{ value: selectProviderReasoningEffort(selected, loaded.session.reasoningEffort) }];
|
|
3982
|
+
const currentEffort = options.some((option) => option.value === loaded.session.reasoningEffort)
|
|
3970
3983
|
? loaded.session.reasoningEffort
|
|
3971
|
-
: selected.defaultReasoningEffort;
|
|
3984
|
+
: selected.defaultReasoningEffort ?? options[0]?.value;
|
|
3972
3985
|
const card = this.cardRenderer.renderReasoningSelector({
|
|
3973
3986
|
sessionId,
|
|
3974
3987
|
contextKey,
|
|
3975
3988
|
modelProvider,
|
|
3976
3989
|
model,
|
|
3977
3990
|
currentEffort,
|
|
3978
|
-
options
|
|
3991
|
+
options,
|
|
3979
3992
|
permissionMode,
|
|
3980
3993
|
unifiedSettings: true,
|
|
3981
3994
|
});
|
|
@@ -4053,10 +4066,11 @@ export class ProxySessionController {
|
|
|
4053
4066
|
}
|
|
4054
4067
|
async assertProviderModelSettings(loaded, modelProvider, model, effort) {
|
|
4055
4068
|
await this.assertModelProvider(loaded, modelProvider);
|
|
4056
|
-
const selected = (await loaded.runtime.listModels()).find((candidate) => candidate.id === model);
|
|
4069
|
+
const selected = (await loaded.runtime.listModels(modelProvider, model)).find((candidate) => candidate.id === model);
|
|
4057
4070
|
if (!selected)
|
|
4058
4071
|
throw new Error(`未知模型:${model}`);
|
|
4059
|
-
if (
|
|
4072
|
+
if (selected.supportedReasoningEfforts.length > 0
|
|
4073
|
+
&& !selected.supportedReasoningEfforts.some((option) => option.value === effort)) {
|
|
4060
4074
|
const supported = selected.supportedReasoningEfforts.map((option) => option.value).join("、") || "无";
|
|
4061
4075
|
throw new Error(`模型 ${model} 不支持思考强度 ${effort}。支持的强度:${supported}`);
|
|
4062
4076
|
}
|
|
@@ -4087,13 +4101,14 @@ export class ProxySessionController {
|
|
|
4087
4101
|
? this.requireSession(contextKey, options.sessionId)
|
|
4088
4102
|
: this.requireCurrentSession(contextKey);
|
|
4089
4103
|
const loaded = await this.loadSession(record);
|
|
4090
|
-
const models = await loaded.runtime.listModels();
|
|
4104
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
4091
4105
|
const selected = models.find((item) => item.id === model);
|
|
4092
4106
|
if (!selected)
|
|
4093
4107
|
throw new Error(`未知模型:${model}`);
|
|
4094
4108
|
const currentEffort = loaded.session.reasoningEffort;
|
|
4095
4109
|
const compatible = currentEffort
|
|
4096
|
-
? selected.supportedReasoningEfforts.
|
|
4110
|
+
? selected.supportedReasoningEfforts.length === 0
|
|
4111
|
+
|| selected.supportedReasoningEfforts.some((option) => option.value === currentEffort)
|
|
4097
4112
|
: false;
|
|
4098
4113
|
const nextEffort = compatible ? currentEffort : selected.defaultReasoningEffort;
|
|
4099
4114
|
await loaded.runtime.setModel(loaded.record.localSessionId, model);
|
|
@@ -4474,7 +4489,7 @@ export class ProxySessionController {
|
|
|
4474
4489
|
? this.requireSession(contextKey, options.sessionId)
|
|
4475
4490
|
: this.requireCurrentSession(contextKey);
|
|
4476
4491
|
const loaded = await this.loadSession(record);
|
|
4477
|
-
const models = await loaded.runtime.listModels();
|
|
4492
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
4478
4493
|
const currentModel = models.find((item) => item.id === loaded.session.model)
|
|
4479
4494
|
?? models.find((item) => item.isDefault);
|
|
4480
4495
|
if (!currentModel)
|
|
@@ -4527,6 +4542,73 @@ export class ProxySessionController {
|
|
|
4527
4542
|
? `已切换为自动执行模式,并已保存为 ${loaded.record.agentName} Agent 的默认设置。`
|
|
4528
4543
|
: `已切换为执行前确认模式,并已保存为 ${loaded.record.agentName} Agent 的默认设置。`);
|
|
4529
4544
|
}
|
|
4545
|
+
taskTurnIds(current) {
|
|
4546
|
+
const ids = this.store.listTaskTurnGraphIndex(current.localSessionId).map((turn) => turn.turnId);
|
|
4547
|
+
if (current.lastTurnStatus === "running" && current.lastTurnId && !ids.includes(current.lastTurnId)) {
|
|
4548
|
+
ids.unshift(current.lastTurnId);
|
|
4549
|
+
}
|
|
4550
|
+
return ids;
|
|
4551
|
+
}
|
|
4552
|
+
runningTurnSnapshot(current, turnId) {
|
|
4553
|
+
const snapshot = turnViewSnapshot(this.store.getTurnSnapshot(turnId));
|
|
4554
|
+
if (snapshot)
|
|
4555
|
+
return snapshot;
|
|
4556
|
+
const attempt = this.store.findIncompleteTurnAttemptByTurnId(turnId);
|
|
4557
|
+
const startedAt = Date.parse(attempt?.createdAt ?? "");
|
|
4558
|
+
return {
|
|
4559
|
+
sessionId: current.localSessionId,
|
|
4560
|
+
turnId,
|
|
4561
|
+
taskTitle: current.title,
|
|
4562
|
+
prompt: attempt?.promptText ?? current.title,
|
|
4563
|
+
status: "running",
|
|
4564
|
+
startedAt: Number.isFinite(startedAt) ? startedAt : Date.now(),
|
|
4565
|
+
assistantText: "", plan: [], activities: [], completedTools: [], failedTools: [], fileSummary: [],
|
|
4566
|
+
};
|
|
4567
|
+
}
|
|
4568
|
+
async openTurnDetails(contextKey, reference) {
|
|
4569
|
+
const current = this.requireCurrentSession(contextKey);
|
|
4570
|
+
let turnId;
|
|
4571
|
+
if (/^\d+$/.test(reference)) {
|
|
4572
|
+
const index = Number(reference);
|
|
4573
|
+
const runningCount = current.lastTurnStatus === "running" && current.lastTurnId ? 1 : 0;
|
|
4574
|
+
const hasMore = index > runningCount && this.isCodexSession(current)
|
|
4575
|
+
? await this.hydrateTaskTurnHistory(current, index - runningCount)
|
|
4576
|
+
: false;
|
|
4577
|
+
turnId = this.taskTurnIds(current)[index - 1];
|
|
4578
|
+
if (!turnId) {
|
|
4579
|
+
throw new Error(hasMore
|
|
4580
|
+
? "轮次历史暂未加载完成,请稍后重试。"
|
|
4581
|
+
: `未找到序号为 ${reference} 的轮次。请发送 /turn 查看当前轮次序号。`);
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
else {
|
|
4585
|
+
const snapshot = turnViewSnapshot(this.store.getTurnSnapshot(reference));
|
|
4586
|
+
if (snapshot && (snapshot.sessionId === current.localSessionId
|
|
4587
|
+
|| this.store.hasTaskHistoryTurn(current.localSessionId, reference))) {
|
|
4588
|
+
turnId = reference;
|
|
4589
|
+
}
|
|
4590
|
+
else if (current.lastTurnStatus === "running" && current.lastTurnId === reference) {
|
|
4591
|
+
turnId = reference;
|
|
4592
|
+
}
|
|
4593
|
+
else if (this.isCodexSession(current) && (!snapshot || this.store.getForkHistorySource(current.localSessionId))) {
|
|
4594
|
+
const hasMore = await this.hydrateTaskTurnHistory(current, 0, reference);
|
|
4595
|
+
if (this.store.hasTaskHistoryTurn(current.localSessionId, reference))
|
|
4596
|
+
turnId = reference;
|
|
4597
|
+
else if (hasMore)
|
|
4598
|
+
throw new Error("轮次历史暂未加载完成,请稍后重试。");
|
|
4599
|
+
}
|
|
4600
|
+
if (!turnId && snapshot)
|
|
4601
|
+
throw new Error("这个 Turn 不属于当前任务的历史。请先切换到对应任务。");
|
|
4602
|
+
if (!turnId)
|
|
4603
|
+
throw new Error(`未找到当前任务的 Turn:${reference}`);
|
|
4604
|
+
}
|
|
4605
|
+
if (current.lastTurnStatus === "running" && current.lastTurnId === turnId
|
|
4606
|
+
&& !this.store.getTurnPromptSummary(turnId)) {
|
|
4607
|
+
await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderTurnDetails(this.runningTurnSnapshot(current, turnId)));
|
|
4608
|
+
return;
|
|
4609
|
+
}
|
|
4610
|
+
await this.outbound.showDetails(contextKey, turnId);
|
|
4611
|
+
}
|
|
4530
4612
|
async openResetHistory(contextKey, options = {}) {
|
|
4531
4613
|
const current = this.requireCurrentSession(contextKey);
|
|
4532
4614
|
if (options.expectedSessionId && current.localSessionId !== options.expectedSessionId) {
|
|
@@ -4543,27 +4625,11 @@ export class ProxySessionController {
|
|
|
4543
4625
|
&& !completedTurnIds.has(current.lastTurnId)
|
|
4544
4626
|
? current.lastTurnId
|
|
4545
4627
|
: undefined;
|
|
4546
|
-
const persistedRunningSnapshot = runningTurnId
|
|
4547
|
-
? turnViewSnapshot(this.store.getTurnSnapshot(runningTurnId))
|
|
4548
|
-
: undefined;
|
|
4549
4628
|
const runningAttempt = runningTurnId
|
|
4550
4629
|
? this.store.findIncompleteTurnAttemptByTurnId(runningTurnId)
|
|
4551
4630
|
: undefined;
|
|
4552
|
-
const attemptStartedAt = Date.parse(runningAttempt?.createdAt ?? "");
|
|
4553
4631
|
const runningSnapshot = runningTurnId
|
|
4554
|
-
?
|
|
4555
|
-
sessionId: current.localSessionId,
|
|
4556
|
-
turnId: runningTurnId,
|
|
4557
|
-
prompt: runningAttempt?.promptText ?? current.title,
|
|
4558
|
-
status: "running",
|
|
4559
|
-
startedAt: Number.isFinite(attemptStartedAt) ? attemptStartedAt : Date.now(),
|
|
4560
|
-
assistantText: "",
|
|
4561
|
-
plan: [],
|
|
4562
|
-
activities: [],
|
|
4563
|
-
completedTools: [],
|
|
4564
|
-
failedTools: [],
|
|
4565
|
-
fileSummary: [],
|
|
4566
|
-
}
|
|
4632
|
+
? this.runningTurnSnapshot(current, runningTurnId)
|
|
4567
4633
|
: undefined;
|
|
4568
4634
|
const runningTurn = runningTurnId && runningSnapshot
|
|
4569
4635
|
? {
|
|
@@ -5165,7 +5231,7 @@ export class ProxySessionController {
|
|
|
5165
5231
|
await this.assertSessionTurnOwnership(existing, runtime);
|
|
5166
5232
|
this.store.attachSessionToContext(contextKey, existing.localSessionId);
|
|
5167
5233
|
this.store.setCurrentSession(contextKey, existing.localSessionId);
|
|
5168
|
-
this.outbound.registerSession(existing.localSessionId, contextKey, existing.title, existing.cwd, this.agentLabel(existing.agentName));
|
|
5234
|
+
this.outbound.registerSession(existing.localSessionId, contextKey, existing.title, existing.cwd, this.agentLabel(existing.agentName), existing.model);
|
|
5169
5235
|
await this.outbound.sendText(contextKey, `已切换到任务:${existing.title ?? existing.remoteSessionId ?? taskId}`);
|
|
5170
5236
|
return;
|
|
5171
5237
|
}
|
|
@@ -5192,7 +5258,7 @@ export class ProxySessionController {
|
|
|
5192
5258
|
lastTurnStatus: mapRemoteTurnStatus(remote.lastTurnStatus),
|
|
5193
5259
|
});
|
|
5194
5260
|
this.store.setCurrentSession(contextKey, localSessionId);
|
|
5195
|
-
this.outbound.registerSession(localSessionId, contextKey, remote.title ?? remote.preview, remote.cwd, this.agentLabel(agentName));
|
|
5261
|
+
this.outbound.registerSession(localSessionId, contextKey, remote.title ?? remote.preview, remote.cwd, this.agentLabel(agentName), remote.model);
|
|
5196
5262
|
await this.outbound.sendText(contextKey, `已切换到任务:${remote.title ?? remote.preview ?? remote.id}。历史消息不会重新发送。`);
|
|
5197
5263
|
}
|
|
5198
5264
|
async stopSessionReference(contextKey, taskId) {
|
|
@@ -5454,6 +5520,7 @@ export class ProxySessionController {
|
|
|
5454
5520
|
current = context.currentSessionId ? this.store.getSession(context.currentSessionId) : undefined;
|
|
5455
5521
|
}
|
|
5456
5522
|
let remote;
|
|
5523
|
+
let metrics;
|
|
5457
5524
|
if (current?.status === "running") {
|
|
5458
5525
|
const runtime = this.runtimes.forAgent(current.agentName);
|
|
5459
5526
|
if (runtime.getSession(current.localSessionId)) {
|
|
@@ -5491,6 +5558,14 @@ export class ProxySessionController {
|
|
|
5491
5558
|
this.logger.warn({ error, sessionId: current.localSessionId }, "Failed to inspect Agent goal status.");
|
|
5492
5559
|
}
|
|
5493
5560
|
}
|
|
5561
|
+
if (runtime.readRemoteSessionMetrics) {
|
|
5562
|
+
try {
|
|
5563
|
+
metrics = await runtime.readRemoteSessionMetrics(current.remoteSessionId);
|
|
5564
|
+
}
|
|
5565
|
+
catch (error) {
|
|
5566
|
+
this.logger.warn({ error, sessionId: current.localSessionId }, "Failed to inspect App Server task metrics.");
|
|
5567
|
+
}
|
|
5568
|
+
}
|
|
5494
5569
|
}
|
|
5495
5570
|
if (current)
|
|
5496
5571
|
current = mergeRemoteTaskStatus(current, remote);
|
|
@@ -5517,7 +5592,7 @@ export class ProxySessionController {
|
|
|
5517
5592
|
const resultLabel = remoteActive
|
|
5518
5593
|
? "执行中"
|
|
5519
5594
|
: remoteTurnStatusLabel(remote?.lastTurnStatus ?? current.lastTurnStatus);
|
|
5520
|
-
taskLines.push(`**标题**:${cardCode(current.title ?? "未命名任务")}`, `**工作目录**:${cardCode(current.cwd)}`, `**Provider / 模型 / 思考强度**:${cardCode(current.modelProvider ?? "Agent 默认")} / ${cardCode(current.model ?? "默认")} / ${cardCode(current.reasoningEffort ?? "自动")}`, `**状态 / 最近结果**:${statusLabel} / ${resultLabel}`, `**Agent**:${cardCode(agent.title)}`, `**权限 / 任务范围**:${current.permissionMode === "confirm" ? "执行前确认" : "自动执行"} / ${detectProjectlessWorkspace(current.cwd) ? "未指定项目" : "指定项目"}`, `**App Server 任务 ID**:${cardCode(current.remoteSessionId ?? "尚未创建")}`, `**当前执行 / 排队消息**:${activeTurnId ? cardCode(activeTurnId) : "无"} / ${queued} 条`, `**创建时间 / 最近活动**:${formatStatusTime(current.createdAt)} / ${formatStatusTime(current.updatedAt)}`);
|
|
5595
|
+
taskLines.push(`**标题**:${cardCode(current.title ?? "未命名任务")}`, `**工作目录**:${cardCode(current.cwd)}`, `**Provider / 模型 / 思考强度**:${cardCode(current.modelProvider ?? "Agent 默认")} / ${cardCode(current.model ?? "默认")} / ${cardCode(current.reasoningEffort ?? "自动")}`, `**状态 / 最近结果**:${statusLabel} / ${resultLabel}`, `**Agent**:${cardCode(agent.title)}`, `**权限 / 任务范围**:${current.permissionMode === "confirm" ? "执行前确认" : "自动执行"} / ${detectProjectlessWorkspace(current.cwd) ? "未指定项目" : "指定项目"}`, `**App Server 任务 ID**:${cardCode(current.remoteSessionId ?? "尚未创建")}`, statusTaskMetricsLine(metrics?.turnCount ?? this.store.countTaskTurns(current.localSessionId), metrics?.storageBytes), `**当前执行 / 排队消息**:${activeTurnId ? cardCode(activeTurnId) : "无"} / ${queued} 条`, `**创建时间 / 最近活动**:${formatStatusTime(current.createdAt)} / ${formatStatusTime(current.updatedAt)}`);
|
|
5521
5596
|
}
|
|
5522
5597
|
const sections = [
|
|
5523
5598
|
{ title: targetSessionId ? "指定任务" : "当前任务", lines: taskLines },
|
|
@@ -5575,6 +5650,16 @@ export class ProxySessionController {
|
|
|
5575
5650
|
}
|
|
5576
5651
|
async statusForCodexTask(contextKey, reference, options = {}) {
|
|
5577
5652
|
const { agentName, remote } = await this.resolveRemoteCodexSession(reference, { view: "latest-full" });
|
|
5653
|
+
const runtime = this.runtimes.forAgent(agentName);
|
|
5654
|
+
let metrics;
|
|
5655
|
+
if (runtime.readRemoteSessionMetrics) {
|
|
5656
|
+
try {
|
|
5657
|
+
metrics = await runtime.readRemoteSessionMetrics(remote.id);
|
|
5658
|
+
}
|
|
5659
|
+
catch (error) {
|
|
5660
|
+
this.logger.warn({ error, remoteSessionId: remote.id }, "Failed to inspect App Server task metrics.");
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5578
5663
|
const actionReference = remoteSessionReference(agentName, remote.id);
|
|
5579
5664
|
const sections = [
|
|
5580
5665
|
{
|
|
@@ -5585,6 +5670,7 @@ export class ProxySessionController {
|
|
|
5585
5670
|
`**状态 / 当前任务**:${remoteSessionDetailStatus(remote)} / 未切换`,
|
|
5586
5671
|
`**Agent**:${cardCode(agentName)}`,
|
|
5587
5672
|
`**App Server 任务 ID**:${cardCode(remote.id)}`,
|
|
5673
|
+
statusTaskMetricsLine(metrics?.turnCount, metrics?.storageBytes),
|
|
5588
5674
|
`**最近回合**:${cardCode(remote.lastTurnId ?? "无")} ${remoteTurnStatusLabel(remote.lastTurnStatus)}`,
|
|
5589
5675
|
`**创建时间 / 最近活动**:${formatRemoteTime(remote.createdAt)} / ${formatRemoteTime(latestRemoteTimestamp(remote.recencyAt, remote.updatedAt))}`,
|
|
5590
5676
|
],
|
|
@@ -6578,6 +6664,11 @@ function statusExcerpt(value, maxLength) {
|
|
|
6578
6664
|
function formatDuration(milliseconds) {
|
|
6579
6665
|
return milliseconds < 1_000 ? `${milliseconds}ms` : `${(milliseconds / 1_000).toFixed(1)}s`;
|
|
6580
6666
|
}
|
|
6667
|
+
function statusTaskMetricsLine(turnCount, storageBytes) {
|
|
6668
|
+
const turns = turnCount === undefined ? "未知" : `${Math.max(0, Math.round(turnCount)).toLocaleString("en-US")} 轮`;
|
|
6669
|
+
const storage = storageBytes === undefined ? "未知" : formatStorageSize(storageBytes);
|
|
6670
|
+
return `**已执行轮次 / 磁盘占用**:${turns} / ${storage}`;
|
|
6671
|
+
}
|
|
6581
6672
|
function formatStatusTime(value) {
|
|
6582
6673
|
const date = new Date(value);
|
|
6583
6674
|
if (Number.isNaN(date.getTime()))
|
|
@@ -7012,6 +7103,21 @@ function deduplicateFileReferences(files) {
|
|
|
7012
7103
|
return true;
|
|
7013
7104
|
});
|
|
7014
7105
|
}
|
|
7106
|
+
function selectProviderModel(models, previousModel) {
|
|
7107
|
+
return models.find((model) => model.id === previousModel)
|
|
7108
|
+
?? models.find((model) => model.isDefault)
|
|
7109
|
+
?? models[0];
|
|
7110
|
+
}
|
|
7111
|
+
function selectProviderReasoningEffort(model, previousEffort) {
|
|
7112
|
+
if (previousEffort && (model.supportedReasoningEfforts.length === 0
|
|
7113
|
+
|| model.supportedReasoningEfforts.some((option) => option.value === previousEffort))) {
|
|
7114
|
+
return previousEffort;
|
|
7115
|
+
}
|
|
7116
|
+
return model.defaultReasoningEffort
|
|
7117
|
+
?? model.supportedReasoningEfforts[0]?.value
|
|
7118
|
+
?? previousEffort
|
|
7119
|
+
?? "medium";
|
|
7120
|
+
}
|
|
7015
7121
|
function recoveryCardPrompt(prompt) {
|
|
7016
7122
|
return `恢复重启前的任务:${prompt}`;
|
|
7017
7123
|
}
|