@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
|
@@ -59,9 +59,11 @@ import type { AgentRuntimeRegistry } from "../runtime/AgentRuntimeRegistry.js";
|
|
|
59
59
|
import type {
|
|
60
60
|
AgentRuntime,
|
|
61
61
|
ApprovalDecision,
|
|
62
|
+
ModelOption,
|
|
62
63
|
PermissionMode,
|
|
63
64
|
RemoteCompletedTurnSummary,
|
|
64
65
|
RemoteSessionActivity,
|
|
66
|
+
RemoteSessionMetrics,
|
|
65
67
|
RemoteSessionPage,
|
|
66
68
|
RemoteSessionSummary,
|
|
67
69
|
RuntimeGoal,
|
|
@@ -87,6 +89,7 @@ import {
|
|
|
87
89
|
type TurnAnchorRecord,
|
|
88
90
|
} from "../state/StateStore.js";
|
|
89
91
|
import { createId } from "../utils/id.js";
|
|
92
|
+
import { formatStorageSize } from "../utils/formatStorageSize.js";
|
|
90
93
|
import { truncateMiddle, truncateText } from "../utils/markdown.js";
|
|
91
94
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
92
95
|
import {
|
|
@@ -257,7 +260,7 @@ const HELP_COMMAND_SECTIONS: Array<{
|
|
|
257
260
|
usage: "[序号或任务 ID]",
|
|
258
261
|
description: "从当前或指定任务最近完成轮次创建分支",
|
|
259
262
|
},
|
|
260
|
-
{ command: "/turns", description: "浏览历史轮次,并可 Reset
|
|
263
|
+
{ command: "/turns", usage: "[Turn ID 或序号]", description: "浏览历史轮次,并可 Reset 对话上下文;带参数查看运行详情" },
|
|
261
264
|
{
|
|
262
265
|
command: "/title",
|
|
263
266
|
usage: "<新标题>",
|
|
@@ -1433,6 +1436,7 @@ export class ProxySessionController {
|
|
|
1433
1436
|
scopedRecord.title,
|
|
1434
1437
|
scopedRecord.cwd,
|
|
1435
1438
|
this.agentLabel(scopedRecord.agentName),
|
|
1439
|
+
scopedRecord.model,
|
|
1436
1440
|
);
|
|
1437
1441
|
const promptMessageId = await this.outbound.withReplyTarget(
|
|
1438
1442
|
responseContextKey,
|
|
@@ -1680,16 +1684,10 @@ export class ProxySessionController {
|
|
|
1680
1684
|
const loaded = await this.loadSession(record);
|
|
1681
1685
|
if (setting === "provider" && nextValue) {
|
|
1682
1686
|
await this.assertModelProvider(loaded, nextValue);
|
|
1683
|
-
const models = await loaded.runtime.listModels();
|
|
1684
|
-
const model = models
|
|
1685
|
-
?? models.find((candidate) => candidate.isDefault);
|
|
1687
|
+
const models = await loaded.runtime.listModels(nextValue, loaded.session.model);
|
|
1688
|
+
const model = selectProviderModel(models, loaded.session.model);
|
|
1686
1689
|
if (!model) throw new Error("The current runtime has no model available for a Provider change.");
|
|
1687
|
-
const effort = model.
|
|
1688
|
-
(candidate) => candidate.value === loaded.session.reasoningEffort,
|
|
1689
|
-
)
|
|
1690
|
-
? loaded.session.reasoningEffort
|
|
1691
|
-
: model.defaultReasoningEffort ?? model.supportedReasoningEfforts[0]?.value;
|
|
1692
|
-
if (!effort) throw new Error(`Model ${model.id} has no reasoning effort available for a Provider change.`);
|
|
1690
|
+
const effort = selectProviderReasoningEffort(model, loaded.session.reasoningEffort);
|
|
1693
1691
|
await this.changeProviderSettings(loaded, {
|
|
1694
1692
|
modelProvider: nextValue,
|
|
1695
1693
|
model: model.id,
|
|
@@ -1697,13 +1695,12 @@ export class ProxySessionController {
|
|
|
1697
1695
|
permissionMode: loaded.session.permissionMode,
|
|
1698
1696
|
});
|
|
1699
1697
|
} else if (setting === "model" && nextValue) {
|
|
1700
|
-
const models = await loaded.runtime.listModels();
|
|
1698
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
1701
1699
|
const selected = models.find((candidate) => candidate.id === nextValue);
|
|
1702
1700
|
if (!selected) throw new Error(`Unknown model: ${nextValue}`);
|
|
1703
1701
|
const currentEffort = loaded.session.reasoningEffort;
|
|
1704
|
-
const nextEffort = currentEffort && selected.supportedReasoningEfforts.
|
|
1705
|
-
(candidate) => candidate.value === currentEffort
|
|
1706
|
-
)
|
|
1702
|
+
const nextEffort = currentEffort && (selected.supportedReasoningEfforts.length === 0
|
|
1703
|
+
|| selected.supportedReasoningEfforts.some((candidate) => candidate.value === currentEffort))
|
|
1707
1704
|
? currentEffort
|
|
1708
1705
|
: selected.defaultReasoningEffort;
|
|
1709
1706
|
await loaded.runtime.setModel(record.localSessionId, nextValue);
|
|
@@ -1712,7 +1709,7 @@ export class ProxySessionController {
|
|
|
1712
1709
|
}
|
|
1713
1710
|
this.store.updateRuntimeSession(record.localSessionId, { model: nextValue, reasoningEffort: nextEffort });
|
|
1714
1711
|
} else if (setting === "thinking" && nextValue) {
|
|
1715
|
-
const models = await loaded.runtime.listModels();
|
|
1712
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
1716
1713
|
const currentModel = models.find((candidate) => candidate.id === loaded.session.model)
|
|
1717
1714
|
?? models.find((candidate) => candidate.isDefault);
|
|
1718
1715
|
if (!currentModel) throw new Error("The current runtime has no model with configurable reasoning effort.");
|
|
@@ -1731,7 +1728,7 @@ export class ProxySessionController {
|
|
|
1731
1728
|
}
|
|
1732
1729
|
const session = this.store.getSession(record.localSessionId) ?? record;
|
|
1733
1730
|
if (setting && setting !== "provider") await this.persistAgentExecutionDefaults(session.localSessionId);
|
|
1734
|
-
const models = await loaded.runtime.listModels();
|
|
1731
|
+
const models = await loaded.runtime.listModels(session.modelProvider, session.model);
|
|
1735
1732
|
const currentModel = models.find((candidate) => candidate.id === session.model)
|
|
1736
1733
|
?? models.find((candidate) => candidate.isDefault);
|
|
1737
1734
|
const providers = loaded.runtime.listModelProviders
|
|
@@ -2077,8 +2074,13 @@ export class ProxySessionController {
|
|
|
2077
2074
|
await this.setGroupMute(contextKey, command.enabled);
|
|
2078
2075
|
return;
|
|
2079
2076
|
case "turns":
|
|
2080
|
-
|
|
2081
|
-
|
|
2077
|
+
if (command.turnReference) {
|
|
2078
|
+
await this.outbound.sendText(contextKey, "正在读取轮次运行信息,请稍后。");
|
|
2079
|
+
await this.openTurnDetails(contextKey, command.turnReference);
|
|
2080
|
+
} else {
|
|
2081
|
+
await this.outbound.sendText(contextKey, LIST_TURNS_STARTED_MESSAGE);
|
|
2082
|
+
await this.openResetHistory(contextKey);
|
|
2083
|
+
}
|
|
2082
2084
|
return;
|
|
2083
2085
|
case "model":
|
|
2084
2086
|
await this.openExecutionSettings(contextKey, "model");
|
|
@@ -2116,6 +2118,7 @@ export class ProxySessionController {
|
|
|
2116
2118
|
session.title,
|
|
2117
2119
|
session.cwd,
|
|
2118
2120
|
this.agentLabel(session.agentName),
|
|
2121
|
+
session.model,
|
|
2119
2122
|
);
|
|
2120
2123
|
if (!session.lastTurnId) continue;
|
|
2121
2124
|
void this.outbound.resumeDelivery(session.localSessionId, turnContextKey, session.lastTurnId).catch((error: unknown) => {
|
|
@@ -2202,6 +2205,7 @@ export class ProxySessionController {
|
|
|
2202
2205
|
session.title,
|
|
2203
2206
|
session.cwd,
|
|
2204
2207
|
this.agentLabel(session.agentName),
|
|
2208
|
+
session.model,
|
|
2205
2209
|
);
|
|
2206
2210
|
}
|
|
2207
2211
|
try {
|
|
@@ -2330,6 +2334,7 @@ export class ProxySessionController {
|
|
|
2330
2334
|
session.title,
|
|
2331
2335
|
session.cwd,
|
|
2332
2336
|
this.agentLabel(session.agentName),
|
|
2337
|
+
session.model,
|
|
2333
2338
|
);
|
|
2334
2339
|
if (announce) {
|
|
2335
2340
|
await this.outbound.withReplyTarget(contextKey, replyTarget, () =>
|
|
@@ -2611,6 +2616,7 @@ export class ProxySessionController {
|
|
|
2611
2616
|
record.title,
|
|
2612
2617
|
record.cwd,
|
|
2613
2618
|
this.agentLabel(record.agentName),
|
|
2619
|
+
record.model,
|
|
2614
2620
|
);
|
|
2615
2621
|
await this.outbound.startPendingTurn(
|
|
2616
2622
|
record.localSessionId,
|
|
@@ -2969,6 +2975,7 @@ export class ProxySessionController {
|
|
|
2969
2975
|
forkTitle,
|
|
2970
2976
|
source.cwd,
|
|
2971
2977
|
this.agentLabel(source.agentName),
|
|
2978
|
+
source.model,
|
|
2972
2979
|
);
|
|
2973
2980
|
|
|
2974
2981
|
try {
|
|
@@ -3329,6 +3336,7 @@ export class ProxySessionController {
|
|
|
3329
3336
|
forked.title ?? plan.forkTitle,
|
|
3330
3337
|
plan.cwd,
|
|
3331
3338
|
this.agentLabel(plan.agentName),
|
|
3339
|
+
plan.model,
|
|
3332
3340
|
);
|
|
3333
3341
|
this.store.audit(contextKey, "session_forked", {
|
|
3334
3342
|
sourceLocalSessionId: plan.source?.localSessionId,
|
|
@@ -3404,14 +3412,15 @@ export class ProxySessionController {
|
|
|
3404
3412
|
private async hydrateTaskTurnHistory(
|
|
3405
3413
|
current: SessionRecord,
|
|
3406
3414
|
requiredCount = RESET_HISTORY_PAGE_SIZE,
|
|
3415
|
+
targetTurnId?: string,
|
|
3407
3416
|
): Promise<boolean> {
|
|
3408
3417
|
const existing = this.turnHistoryHydrations.get(current.localSessionId);
|
|
3409
3418
|
if (existing) await existing;
|
|
3410
3419
|
const hydration = (async () => {
|
|
3411
3420
|
if (this.store.getForkHistorySource(current.localSessionId)) {
|
|
3412
|
-
return this.hydrateForkTurnHistory(current.contextKey, current, requiredCount);
|
|
3421
|
+
return this.hydrateForkTurnHistory(current.contextKey, current, requiredCount, new Set(), targetTurnId);
|
|
3413
3422
|
}
|
|
3414
|
-
return this.hydrateRemoteTurnHistory(current.contextKey, current, requiredCount);
|
|
3423
|
+
return this.hydrateRemoteTurnHistory(current.contextKey, current, requiredCount, targetTurnId);
|
|
3415
3424
|
})();
|
|
3416
3425
|
this.turnHistoryHydrations.set(current.localSessionId, hydration);
|
|
3417
3426
|
try {
|
|
@@ -3428,6 +3437,7 @@ export class ProxySessionController {
|
|
|
3428
3437
|
current: SessionRecord,
|
|
3429
3438
|
requiredCount: number,
|
|
3430
3439
|
visited = new Set<string>(),
|
|
3440
|
+
targetTurnId?: string,
|
|
3431
3441
|
): Promise<boolean> {
|
|
3432
3442
|
if (visited.has(current.localSessionId)) return false;
|
|
3433
3443
|
visited.add(current.localSessionId);
|
|
@@ -3456,7 +3466,7 @@ export class ProxySessionController {
|
|
|
3456
3466
|
? Math.max(0, localAnchorIndex)
|
|
3457
3467
|
: localAnchorIndex >= 0 ? localAnchorIndex : availableCount;
|
|
3458
3468
|
const neededInheritedCount = Math.max(0, requiredCount - ownTurnCount);
|
|
3459
|
-
if (localAnchorIndex >= 0 && availableCount >= requiredCount
|
|
3469
|
+
if (!targetTurnId && localAnchorIndex >= 0 && availableCount >= requiredCount
|
|
3460
3470
|
&& (!state.anchorFound || (state.loadedCount ?? 0) >= neededInheritedCount)) return !state.complete;
|
|
3461
3471
|
const sourceSession = source.sourceLocalSessionId
|
|
3462
3472
|
? this.store.getSession(source.sourceLocalSessionId)
|
|
@@ -3469,6 +3479,7 @@ export class ProxySessionController {
|
|
|
3469
3479
|
sourceSession.contextKey, sourceSession,
|
|
3470
3480
|
this.store.listTaskTurnGraphIndex(sourceSession.localSessionId).length + requiredCount - availableCount,
|
|
3471
3481
|
visited,
|
|
3482
|
+
targetTurnId,
|
|
3472
3483
|
);
|
|
3473
3484
|
if (!hasMore && this.store.hasImportedForkTurnHistory(sourceSession.localSessionId)) {
|
|
3474
3485
|
this.store.audit(contextKey, "fork_turn_history_imported", {
|
|
@@ -3485,8 +3496,10 @@ export class ProxySessionController {
|
|
|
3485
3496
|
if (runtime.kind !== "codex" || !runtime.listRemoteTurnSummaries) return false;
|
|
3486
3497
|
const seenCursors = new Set<string>();
|
|
3487
3498
|
try {
|
|
3488
|
-
while (!state.complete && (
|
|
3489
|
-
|
|
3499
|
+
while (!state.complete && (targetTurnId
|
|
3500
|
+
? !this.store.hasTaskHistoryTurn(current.localSessionId, targetTurnId)
|
|
3501
|
+
: (state.loadedCount ?? 0) < neededInheritedCount)) {
|
|
3502
|
+
const remaining = targetTurnId ? RESET_HISTORY_PAGE_SIZE : neededInheritedCount - (state.loadedCount ?? 0);
|
|
3490
3503
|
const page = await runtime.listRemoteTurnSummaries(source.sourceRemoteSessionId, {
|
|
3491
3504
|
cursor: state.cursor, limit: Math.min(RESET_HISTORY_PAGE_SIZE, remaining),
|
|
3492
3505
|
});
|
|
@@ -3550,6 +3563,7 @@ export class ProxySessionController {
|
|
|
3550
3563
|
contextKey: string,
|
|
3551
3564
|
current: SessionRecord,
|
|
3552
3565
|
requiredCount: number,
|
|
3566
|
+
targetTurnId?: string,
|
|
3553
3567
|
): Promise<boolean> {
|
|
3554
3568
|
if (!current.remoteSessionId) return false;
|
|
3555
3569
|
const key = JSON.stringify([current.agentName, current.remoteSessionId, "turn-card"]);
|
|
@@ -3561,10 +3575,12 @@ export class ProxySessionController {
|
|
|
3561
3575
|
if (runtime.kind !== "codex" || !runtime.listRemoteTurnSummaries) return false;
|
|
3562
3576
|
const seen = new Set<string>();
|
|
3563
3577
|
try {
|
|
3564
|
-
while (!state.complete && (
|
|
3578
|
+
while (!state.complete && (targetTurnId
|
|
3579
|
+
? !this.store.hasTaskHistoryTurn(current.localSessionId, targetTurnId)
|
|
3580
|
+
: (state.loadedCount ?? 0) < requiredCount)) {
|
|
3565
3581
|
const page = await runtime.listRemoteTurnSummaries(current.remoteSessionId, {
|
|
3566
3582
|
cursor: state.cursor,
|
|
3567
|
-
limit: Math.min(RESET_HISTORY_PAGE_SIZE, requiredCount - (state.loadedCount ?? 0)),
|
|
3583
|
+
limit: targetTurnId ? RESET_HISTORY_PAGE_SIZE : Math.min(RESET_HISTORY_PAGE_SIZE, requiredCount - (state.loadedCount ?? 0)),
|
|
3568
3584
|
});
|
|
3569
3585
|
if (page.nextCursor && (page.nextCursor === state.cursor || seen.has(page.nextCursor))) {
|
|
3570
3586
|
throw new Error("App Server repeated a Turn history cursor.");
|
|
@@ -3838,6 +3854,7 @@ export class ProxySessionController {
|
|
|
3838
3854
|
initialTitle,
|
|
3839
3855
|
sessionCwd,
|
|
3840
3856
|
this.agentLabel(agentName),
|
|
3857
|
+
resolvedExecutionSettings.model,
|
|
3841
3858
|
);
|
|
3842
3859
|
const runtime = this.runtimes.forAgent(agentName);
|
|
3843
3860
|
try {
|
|
@@ -3855,6 +3872,7 @@ export class ProxySessionController {
|
|
|
3855
3872
|
permissionMode: resolvedExecutionSettings.permissionMode ?? "auto",
|
|
3856
3873
|
});
|
|
3857
3874
|
this.persistRuntimeSession(record, session, session.activeTurnId ? "running" : "ready");
|
|
3875
|
+
this.outbound.updateSessionModel(localSessionId, session.model);
|
|
3858
3876
|
const saved = this.store.getSession(localSessionId) ?? record;
|
|
3859
3877
|
if (announce) {
|
|
3860
3878
|
const task = initialTitle ? `${initialTitle}(${session.remoteSessionId})` : session.remoteSessionId;
|
|
@@ -4127,6 +4145,7 @@ export class ProxySessionController {
|
|
|
4127
4145
|
record.title,
|
|
4128
4146
|
record.cwd,
|
|
4129
4147
|
this.agentLabel(record.agentName),
|
|
4148
|
+
record.model,
|
|
4130
4149
|
);
|
|
4131
4150
|
const loading = (async (): Promise<LoadedSession> => {
|
|
4132
4151
|
if (record.lastTurnId) {
|
|
@@ -4277,14 +4296,20 @@ export class ProxySessionController {
|
|
|
4277
4296
|
});
|
|
4278
4297
|
}
|
|
4279
4298
|
|
|
4299
|
+
const enrichedEvent: RuntimeEvent = event.type === "context_compaction"
|
|
4300
|
+
? {
|
|
4301
|
+
...event,
|
|
4302
|
+
turnCount: event.turnCount ?? this.store.countTaskTurns(event.sessionId),
|
|
4303
|
+
}
|
|
4304
|
+
: event;
|
|
4280
4305
|
const presentationEvent = event.type === "turn_failed"
|
|
4281
4306
|
&& isRetryableLlmTurnFailure(event.message)
|
|
4282
4307
|
&& (failedAttempt?.retryCount ?? 0) >= MAX_LLM_TURN_RETRIES
|
|
4283
4308
|
? {
|
|
4284
|
-
...
|
|
4309
|
+
...enrichedEvent,
|
|
4285
4310
|
message: `${event.message}\n\n已自动重试 ${MAX_LLM_TURN_RETRIES} 次,仍未成功。`,
|
|
4286
4311
|
}
|
|
4287
|
-
:
|
|
4312
|
+
: enrichedEvent;
|
|
4288
4313
|
let presentationError: unknown;
|
|
4289
4314
|
try {
|
|
4290
4315
|
await this.outbound.onEvent(presentationEvent);
|
|
@@ -4863,6 +4888,7 @@ export class ProxySessionController {
|
|
|
4863
4888
|
forked.title ?? current.title,
|
|
4864
4889
|
current.cwd,
|
|
4865
4890
|
this.agentLabel(current.agentName),
|
|
4891
|
+
current.model,
|
|
4866
4892
|
);
|
|
4867
4893
|
this.store.audit(contextKey, "session_reset_to_turn", {
|
|
4868
4894
|
localSessionId: current.localSessionId,
|
|
@@ -4926,12 +4952,13 @@ export class ProxySessionController {
|
|
|
4926
4952
|
};
|
|
4927
4953
|
if (record) {
|
|
4928
4954
|
const loaded = await this.loadSession(record);
|
|
4929
|
-
const models = await loaded.runtime.listModels();
|
|
4930
|
-
const currentModel = models.find((model) => model.id === loaded.session.model)
|
|
4931
|
-
?? models.find((model) => model.isDefault);
|
|
4932
4955
|
const providerSupported = loaded.runtime.kind === "codex" && Boolean(loaded.runtime.listModelProviders);
|
|
4933
4956
|
const providers = providerSupported ? await this.modelProviderOptions(loaded) : [];
|
|
4934
4957
|
const currentProvider = loaded.session.modelProvider ?? providers.find((provider) => provider.isDefault)?.id;
|
|
4958
|
+
const models = await loaded.runtime.listModels(currentProvider, loaded.session.model);
|
|
4959
|
+
const currentModel = models.find((model) => model.id === loaded.session.model)
|
|
4960
|
+
?? models.find((model) => model.isDefault)
|
|
4961
|
+
?? models[0];
|
|
4935
4962
|
const currentEffort = currentModel?.supportedReasoningEfforts.some(
|
|
4936
4963
|
(option) => option.value === loaded.session.reasoningEffort,
|
|
4937
4964
|
)
|
|
@@ -5000,16 +5027,10 @@ export class ProxySessionController {
|
|
|
5000
5027
|
await this.outbound.sendText(contextKey, `正在切换到 Provider ${modelProvider},请稍后。`);
|
|
5001
5028
|
const loaded = await this.loadSession(record);
|
|
5002
5029
|
await this.assertModelProvider(loaded, modelProvider);
|
|
5003
|
-
const models = await loaded.runtime.listModels();
|
|
5004
|
-
const model = models
|
|
5005
|
-
?? models.find((candidate) => candidate.isDefault);
|
|
5030
|
+
const models = await loaded.runtime.listModels(modelProvider, loaded.session.model);
|
|
5031
|
+
const model = selectProviderModel(models, loaded.session.model);
|
|
5006
5032
|
if (!model) throw new Error("当前运行时没有可用于 Provider 切换的模型。");
|
|
5007
|
-
const effort = model.
|
|
5008
|
-
(candidate) => candidate.value === loaded.session.reasoningEffort,
|
|
5009
|
-
)
|
|
5010
|
-
? loaded.session.reasoningEffort
|
|
5011
|
-
: model.defaultReasoningEffort ?? model.supportedReasoningEfforts[0]?.value;
|
|
5012
|
-
if (!effort) throw new Error(`模型 ${model.id} 没有可用于 Provider 切换的思考强度。`);
|
|
5033
|
+
const effort = selectProviderReasoningEffort(model, loaded.session.reasoningEffort);
|
|
5013
5034
|
await this.applyProviderSettings(contextKey, {
|
|
5014
5035
|
provider: modelProvider,
|
|
5015
5036
|
model: model.id,
|
|
@@ -5034,11 +5055,11 @@ export class ProxySessionController {
|
|
|
5034
5055
|
): Promise<void> {
|
|
5035
5056
|
const loaded = await this.loadSession(this.requireSession(contextKey, sessionId));
|
|
5036
5057
|
await this.assertModelProvider(loaded, modelProvider);
|
|
5037
|
-
const models = await loaded.runtime.listModels();
|
|
5058
|
+
const models = await loaded.runtime.listModels(modelProvider, loaded.session.model);
|
|
5038
5059
|
const card = this.cardRenderer.renderModelSelector({
|
|
5039
5060
|
sessionId,
|
|
5040
5061
|
contextKey,
|
|
5041
|
-
currentModel: loaded.session.model
|
|
5062
|
+
currentModel: selectProviderModel(models, loaded.session.model)?.id,
|
|
5042
5063
|
reasoningEffort: loaded.session.reasoningEffort,
|
|
5043
5064
|
models,
|
|
5044
5065
|
modelProvider,
|
|
@@ -5058,21 +5079,24 @@ export class ProxySessionController {
|
|
|
5058
5079
|
): Promise<void> {
|
|
5059
5080
|
const loaded = await this.loadSession(this.requireSession(contextKey, sessionId));
|
|
5060
5081
|
await this.assertModelProvider(loaded, modelProvider);
|
|
5061
|
-
const models = await loaded.runtime.listModels();
|
|
5082
|
+
const models = await loaded.runtime.listModels(modelProvider, model);
|
|
5062
5083
|
const selected = models.find((candidate) => candidate.id === model);
|
|
5063
5084
|
if (!selected) throw new Error(`未知模型:${model}`);
|
|
5064
|
-
const
|
|
5085
|
+
const options = selected.supportedReasoningEfforts.length > 0
|
|
5086
|
+
? selected.supportedReasoningEfforts
|
|
5087
|
+
: [{ value: selectProviderReasoningEffort(selected, loaded.session.reasoningEffort) }];
|
|
5088
|
+
const currentEffort = options.some(
|
|
5065
5089
|
(option) => option.value === loaded.session.reasoningEffort,
|
|
5066
5090
|
)
|
|
5067
5091
|
? loaded.session.reasoningEffort
|
|
5068
|
-
: selected.defaultReasoningEffort;
|
|
5092
|
+
: selected.defaultReasoningEffort ?? options[0]?.value;
|
|
5069
5093
|
const card = this.cardRenderer.renderReasoningSelector({
|
|
5070
5094
|
sessionId,
|
|
5071
5095
|
contextKey,
|
|
5072
5096
|
modelProvider,
|
|
5073
5097
|
model,
|
|
5074
5098
|
currentEffort,
|
|
5075
|
-
options
|
|
5099
|
+
options,
|
|
5076
5100
|
permissionMode,
|
|
5077
5101
|
unifiedSettings: true,
|
|
5078
5102
|
});
|
|
@@ -5173,9 +5197,10 @@ export class ProxySessionController {
|
|
|
5173
5197
|
effort: string,
|
|
5174
5198
|
): Promise<void> {
|
|
5175
5199
|
await this.assertModelProvider(loaded, modelProvider);
|
|
5176
|
-
const selected = (await loaded.runtime.listModels()).find((candidate) => candidate.id === model);
|
|
5200
|
+
const selected = (await loaded.runtime.listModels(modelProvider, model)).find((candidate) => candidate.id === model);
|
|
5177
5201
|
if (!selected) throw new Error(`未知模型:${model}`);
|
|
5178
|
-
if (
|
|
5202
|
+
if (selected.supportedReasoningEfforts.length > 0
|
|
5203
|
+
&& !selected.supportedReasoningEfforts.some((option) => option.value === effort)) {
|
|
5179
5204
|
const supported = selected.supportedReasoningEfforts.map((option) => option.value).join("、") || "无";
|
|
5180
5205
|
throw new Error(`模型 ${model} 不支持思考强度 ${effort}。支持的强度:${supported}`);
|
|
5181
5206
|
}
|
|
@@ -5209,12 +5234,13 @@ export class ProxySessionController {
|
|
|
5209
5234
|
? this.requireSession(contextKey, options.sessionId)
|
|
5210
5235
|
: this.requireCurrentSession(contextKey);
|
|
5211
5236
|
const loaded = await this.loadSession(record);
|
|
5212
|
-
const models = await loaded.runtime.listModels();
|
|
5237
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
5213
5238
|
const selected = models.find((item) => item.id === model);
|
|
5214
5239
|
if (!selected) throw new Error(`未知模型:${model}`);
|
|
5215
5240
|
const currentEffort = loaded.session.reasoningEffort;
|
|
5216
5241
|
const compatible = currentEffort
|
|
5217
|
-
? selected.supportedReasoningEfforts.
|
|
5242
|
+
? selected.supportedReasoningEfforts.length === 0
|
|
5243
|
+
|| selected.supportedReasoningEfforts.some((option) => option.value === currentEffort)
|
|
5218
5244
|
: false;
|
|
5219
5245
|
const nextEffort = compatible ? currentEffort : selected.defaultReasoningEffort;
|
|
5220
5246
|
|
|
@@ -5645,7 +5671,7 @@ export class ProxySessionController {
|
|
|
5645
5671
|
? this.requireSession(contextKey, options.sessionId)
|
|
5646
5672
|
: this.requireCurrentSession(contextKey);
|
|
5647
5673
|
const loaded = await this.loadSession(record);
|
|
5648
|
-
const models = await loaded.runtime.listModels();
|
|
5674
|
+
const models = await loaded.runtime.listModels(loaded.session.modelProvider, loaded.session.model);
|
|
5649
5675
|
const currentModel = models.find((item) => item.id === loaded.session.model)
|
|
5650
5676
|
?? models.find((item) => item.isDefault);
|
|
5651
5677
|
if (!currentModel) throw new Error("当前运行时没有可配置思考强度的模型。");
|
|
@@ -5710,6 +5736,71 @@ export class ProxySessionController {
|
|
|
5710
5736
|
);
|
|
5711
5737
|
}
|
|
5712
5738
|
|
|
5739
|
+
private taskTurnIds(current: SessionRecord): string[] {
|
|
5740
|
+
const ids = this.store.listTaskTurnGraphIndex(current.localSessionId).map((turn) => turn.turnId);
|
|
5741
|
+
if (current.lastTurnStatus === "running" && current.lastTurnId && !ids.includes(current.lastTurnId)) {
|
|
5742
|
+
ids.unshift(current.lastTurnId);
|
|
5743
|
+
}
|
|
5744
|
+
return ids;
|
|
5745
|
+
}
|
|
5746
|
+
|
|
5747
|
+
private runningTurnSnapshot(current: SessionRecord, turnId: string): TurnViewState {
|
|
5748
|
+
const snapshot = turnViewSnapshot(this.store.getTurnSnapshot(turnId));
|
|
5749
|
+
if (snapshot) return snapshot;
|
|
5750
|
+
const attempt = this.store.findIncompleteTurnAttemptByTurnId(turnId);
|
|
5751
|
+
const startedAt = Date.parse(attempt?.createdAt ?? "");
|
|
5752
|
+
return {
|
|
5753
|
+
sessionId: current.localSessionId,
|
|
5754
|
+
turnId,
|
|
5755
|
+
taskTitle: current.title,
|
|
5756
|
+
prompt: attempt?.promptText ?? current.title,
|
|
5757
|
+
status: "running",
|
|
5758
|
+
startedAt: Number.isFinite(startedAt) ? startedAt : Date.now(),
|
|
5759
|
+
assistantText: "", plan: [], activities: [], completedTools: [], failedTools: [], fileSummary: [],
|
|
5760
|
+
};
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
private async openTurnDetails(contextKey: string, reference: string): Promise<void> {
|
|
5764
|
+
const current = this.requireCurrentSession(contextKey);
|
|
5765
|
+
let turnId: string | undefined;
|
|
5766
|
+
if (/^\d+$/.test(reference)) {
|
|
5767
|
+
const index = Number(reference);
|
|
5768
|
+
const runningCount = current.lastTurnStatus === "running" && current.lastTurnId ? 1 : 0;
|
|
5769
|
+
const hasMore = index > runningCount && this.isCodexSession(current)
|
|
5770
|
+
? await this.hydrateTaskTurnHistory(current, index - runningCount)
|
|
5771
|
+
: false;
|
|
5772
|
+
turnId = this.taskTurnIds(current)[index - 1];
|
|
5773
|
+
if (!turnId) {
|
|
5774
|
+
throw new Error(hasMore
|
|
5775
|
+
? "轮次历史暂未加载完成,请稍后重试。"
|
|
5776
|
+
: `未找到序号为 ${reference} 的轮次。请发送 /turn 查看当前轮次序号。`);
|
|
5777
|
+
}
|
|
5778
|
+
} else {
|
|
5779
|
+
const snapshot = turnViewSnapshot(this.store.getTurnSnapshot(reference));
|
|
5780
|
+
if (snapshot && (snapshot.sessionId === current.localSessionId
|
|
5781
|
+
|| this.store.hasTaskHistoryTurn(current.localSessionId, reference))) {
|
|
5782
|
+
turnId = reference;
|
|
5783
|
+
} else if (current.lastTurnStatus === "running" && current.lastTurnId === reference) {
|
|
5784
|
+
turnId = reference;
|
|
5785
|
+
} else if (this.isCodexSession(current) && (!snapshot || this.store.getForkHistorySource(current.localSessionId))) {
|
|
5786
|
+
const hasMore = await this.hydrateTaskTurnHistory(current, 0, reference);
|
|
5787
|
+
if (this.store.hasTaskHistoryTurn(current.localSessionId, reference)) turnId = reference;
|
|
5788
|
+
else if (hasMore) throw new Error("轮次历史暂未加载完成,请稍后重试。");
|
|
5789
|
+
}
|
|
5790
|
+
if (!turnId && snapshot) throw new Error("这个 Turn 不属于当前任务的历史。请先切换到对应任务。");
|
|
5791
|
+
if (!turnId) throw new Error(`未找到当前任务的 Turn:${reference}`);
|
|
5792
|
+
}
|
|
5793
|
+
|
|
5794
|
+
if (current.lastTurnStatus === "running" && current.lastTurnId === turnId
|
|
5795
|
+
&& !this.store.getTurnPromptSummary(turnId)) {
|
|
5796
|
+
await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderTurnDetails(
|
|
5797
|
+
this.runningTurnSnapshot(current, turnId),
|
|
5798
|
+
));
|
|
5799
|
+
return;
|
|
5800
|
+
}
|
|
5801
|
+
await this.outbound.showDetails(contextKey, turnId);
|
|
5802
|
+
}
|
|
5803
|
+
|
|
5713
5804
|
private async openResetHistory(
|
|
5714
5805
|
contextKey: string,
|
|
5715
5806
|
options: ResetHistoryCardOptions = {},
|
|
@@ -5729,27 +5820,11 @@ export class ProxySessionController {
|
|
|
5729
5820
|
&& !completedTurnIds.has(current.lastTurnId)
|
|
5730
5821
|
? current.lastTurnId
|
|
5731
5822
|
: undefined;
|
|
5732
|
-
const persistedRunningSnapshot = runningTurnId
|
|
5733
|
-
? turnViewSnapshot(this.store.getTurnSnapshot(runningTurnId))
|
|
5734
|
-
: undefined;
|
|
5735
5823
|
const runningAttempt = runningTurnId
|
|
5736
5824
|
? this.store.findIncompleteTurnAttemptByTurnId(runningTurnId)
|
|
5737
5825
|
: undefined;
|
|
5738
|
-
const attemptStartedAt = Date.parse(runningAttempt?.createdAt ?? "");
|
|
5739
5826
|
const runningSnapshot: TurnViewState | undefined = runningTurnId
|
|
5740
|
-
?
|
|
5741
|
-
sessionId: current.localSessionId,
|
|
5742
|
-
turnId: runningTurnId,
|
|
5743
|
-
prompt: runningAttempt?.promptText ?? current.title,
|
|
5744
|
-
status: "running",
|
|
5745
|
-
startedAt: Number.isFinite(attemptStartedAt) ? attemptStartedAt : Date.now(),
|
|
5746
|
-
assistantText: "",
|
|
5747
|
-
plan: [],
|
|
5748
|
-
activities: [],
|
|
5749
|
-
completedTools: [],
|
|
5750
|
-
failedTools: [],
|
|
5751
|
-
fileSummary: [],
|
|
5752
|
-
}
|
|
5827
|
+
? this.runningTurnSnapshot(current, runningTurnId)
|
|
5753
5828
|
: undefined;
|
|
5754
5829
|
const runningTurn = runningTurnId && runningSnapshot
|
|
5755
5830
|
? {
|
|
@@ -6400,6 +6475,7 @@ export class ProxySessionController {
|
|
|
6400
6475
|
existing.title,
|
|
6401
6476
|
existing.cwd,
|
|
6402
6477
|
this.agentLabel(existing.agentName),
|
|
6478
|
+
existing.model,
|
|
6403
6479
|
);
|
|
6404
6480
|
await this.outbound.sendText(contextKey, `已切换到任务:${existing.title ?? existing.remoteSessionId ?? taskId}`);
|
|
6405
6481
|
return;
|
|
@@ -6433,6 +6509,7 @@ export class ProxySessionController {
|
|
|
6433
6509
|
remote.title ?? remote.preview,
|
|
6434
6510
|
remote.cwd,
|
|
6435
6511
|
this.agentLabel(agentName),
|
|
6512
|
+
remote.model,
|
|
6436
6513
|
);
|
|
6437
6514
|
await this.outbound.sendText(
|
|
6438
6515
|
contextKey,
|
|
@@ -6732,6 +6809,7 @@ export class ProxySessionController {
|
|
|
6732
6809
|
}
|
|
6733
6810
|
|
|
6734
6811
|
let remote: RemoteSessionSummary | undefined;
|
|
6812
|
+
let metrics: RemoteSessionMetrics | undefined;
|
|
6735
6813
|
if (current?.status === "running") {
|
|
6736
6814
|
const runtime = this.runtimes.forAgent(current.agentName);
|
|
6737
6815
|
if (runtime.getSession(current.localSessionId)) {
|
|
@@ -6764,6 +6842,13 @@ export class ProxySessionController {
|
|
|
6764
6842
|
this.logger.warn({ error, sessionId: current.localSessionId }, "Failed to inspect Agent goal status.");
|
|
6765
6843
|
}
|
|
6766
6844
|
}
|
|
6845
|
+
if (runtime.readRemoteSessionMetrics) {
|
|
6846
|
+
try {
|
|
6847
|
+
metrics = await runtime.readRemoteSessionMetrics(current.remoteSessionId);
|
|
6848
|
+
} catch (error) {
|
|
6849
|
+
this.logger.warn({ error, sessionId: current.localSessionId }, "Failed to inspect App Server task metrics.");
|
|
6850
|
+
}
|
|
6851
|
+
}
|
|
6767
6852
|
}
|
|
6768
6853
|
|
|
6769
6854
|
if (current) current = mergeRemoteTaskStatus(current, remote);
|
|
@@ -6798,6 +6883,7 @@ export class ProxySessionController {
|
|
|
6798
6883
|
`**Agent**:${cardCode(agent.title)}`,
|
|
6799
6884
|
`**权限 / 任务范围**:${current.permissionMode === "confirm" ? "执行前确认" : "自动执行"} / ${detectProjectlessWorkspace(current.cwd) ? "未指定项目" : "指定项目"}`,
|
|
6800
6885
|
`**App Server 任务 ID**:${cardCode(current.remoteSessionId ?? "尚未创建")}`,
|
|
6886
|
+
statusTaskMetricsLine(metrics?.turnCount ?? this.store.countTaskTurns(current.localSessionId), metrics?.storageBytes),
|
|
6801
6887
|
`**当前执行 / 排队消息**:${activeTurnId ? cardCode(activeTurnId) : "无"} / ${queued} 条`,
|
|
6802
6888
|
`**创建时间 / 最近活动**:${formatStatusTime(current.createdAt)} / ${formatStatusTime(current.updatedAt)}`,
|
|
6803
6889
|
);
|
|
@@ -6864,6 +6950,15 @@ export class ProxySessionController {
|
|
|
6864
6950
|
options: StatusCardOptions = {},
|
|
6865
6951
|
): Promise<void> {
|
|
6866
6952
|
const { agentName, remote } = await this.resolveRemoteCodexSession(reference, { view: "latest-full" });
|
|
6953
|
+
const runtime = this.runtimes.forAgent(agentName);
|
|
6954
|
+
let metrics: RemoteSessionMetrics | undefined;
|
|
6955
|
+
if (runtime.readRemoteSessionMetrics) {
|
|
6956
|
+
try {
|
|
6957
|
+
metrics = await runtime.readRemoteSessionMetrics(remote.id);
|
|
6958
|
+
} catch (error) {
|
|
6959
|
+
this.logger.warn({ error, remoteSessionId: remote.id }, "Failed to inspect App Server task metrics.");
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6867
6962
|
const actionReference = remoteSessionReference(agentName, remote.id);
|
|
6868
6963
|
const sections: CardSection[] = [
|
|
6869
6964
|
{
|
|
@@ -6874,6 +6969,7 @@ export class ProxySessionController {
|
|
|
6874
6969
|
`**状态 / 当前任务**:${remoteSessionDetailStatus(remote)} / 未切换`,
|
|
6875
6970
|
`**Agent**:${cardCode(agentName)}`,
|
|
6876
6971
|
`**App Server 任务 ID**:${cardCode(remote.id)}`,
|
|
6972
|
+
statusTaskMetricsLine(metrics?.turnCount, metrics?.storageBytes),
|
|
6877
6973
|
`**最近回合**:${cardCode(remote.lastTurnId ?? "无")} ${remoteTurnStatusLabel(remote.lastTurnStatus)}`,
|
|
6878
6974
|
`**创建时间 / 最近活动**:${formatRemoteTime(remote.createdAt)} / ${formatRemoteTime(latestRemoteTimestamp(remote.recencyAt, remote.updatedAt))}`,
|
|
6879
6975
|
],
|
|
@@ -8101,6 +8197,12 @@ function formatDuration(milliseconds: number): string {
|
|
|
8101
8197
|
return milliseconds < 1_000 ? `${milliseconds}ms` : `${(milliseconds / 1_000).toFixed(1)}s`;
|
|
8102
8198
|
}
|
|
8103
8199
|
|
|
8200
|
+
function statusTaskMetricsLine(turnCount: number | undefined, storageBytes: number | undefined): string {
|
|
8201
|
+
const turns = turnCount === undefined ? "未知" : `${Math.max(0, Math.round(turnCount)).toLocaleString("en-US")} 轮`;
|
|
8202
|
+
const storage = storageBytes === undefined ? "未知" : formatStorageSize(storageBytes);
|
|
8203
|
+
return `**已执行轮次 / 磁盘占用**:${turns} / ${storage}`;
|
|
8204
|
+
}
|
|
8205
|
+
|
|
8104
8206
|
function formatStatusTime(value: string): string {
|
|
8105
8207
|
const date = new Date(value);
|
|
8106
8208
|
if (Number.isNaN(date.getTime())) return value;
|
|
@@ -8574,6 +8676,23 @@ function deduplicateFileReferences(
|
|
|
8574
8676
|
});
|
|
8575
8677
|
}
|
|
8576
8678
|
|
|
8679
|
+
function selectProviderModel(models: ModelOption[], previousModel?: string): ModelOption | undefined {
|
|
8680
|
+
return models.find((model) => model.id === previousModel)
|
|
8681
|
+
?? models.find((model) => model.isDefault)
|
|
8682
|
+
?? models[0];
|
|
8683
|
+
}
|
|
8684
|
+
|
|
8685
|
+
function selectProviderReasoningEffort(model: ModelOption, previousEffort?: string): string {
|
|
8686
|
+
if (previousEffort && (model.supportedReasoningEfforts.length === 0
|
|
8687
|
+
|| model.supportedReasoningEfforts.some((option) => option.value === previousEffort))) {
|
|
8688
|
+
return previousEffort;
|
|
8689
|
+
}
|
|
8690
|
+
return model.defaultReasoningEffort
|
|
8691
|
+
?? model.supportedReasoningEfforts[0]?.value
|
|
8692
|
+
?? previousEffort
|
|
8693
|
+
?? "medium";
|
|
8694
|
+
}
|
|
8695
|
+
|
|
8577
8696
|
function recoveryCardPrompt(prompt: string): string {
|
|
8578
8697
|
return `恢复重启前的任务:${prompt}`;
|
|
8579
8698
|
}
|
package/src/runtime/types.ts
CHANGED
|
@@ -73,6 +73,17 @@ export type AgentEvent =
|
|
|
73
73
|
turnId: string;
|
|
74
74
|
lastTokens: number;
|
|
75
75
|
cumulativeTokens: number;
|
|
76
|
+
contextTokens?: number;
|
|
77
|
+
}
|
|
78
|
+
| {
|
|
79
|
+
type: "context_compaction";
|
|
80
|
+
sessionId: string;
|
|
81
|
+
turnId: string;
|
|
82
|
+
phase: "started" | "completed";
|
|
83
|
+
compactionId?: string;
|
|
84
|
+
timestampMs?: number;
|
|
85
|
+
turnCount?: number;
|
|
86
|
+
storageBytes?: number;
|
|
76
87
|
}
|
|
77
88
|
| {
|
|
78
89
|
type: "progress";
|
|
@@ -156,6 +167,11 @@ export interface RemoteSessionActivity {
|
|
|
156
167
|
activeTurnId?: string;
|
|
157
168
|
}
|
|
158
169
|
|
|
170
|
+
export interface RemoteSessionMetrics {
|
|
171
|
+
turnCount: number;
|
|
172
|
+
storageBytes?: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
159
175
|
export interface RuntimeSession {
|
|
160
176
|
localSessionId: string;
|
|
161
177
|
remoteSessionId: string;
|
|
@@ -237,6 +253,7 @@ export interface AgentRuntime {
|
|
|
237
253
|
readSessionMetadata(remoteSessionId: string): Promise<RuntimeSessionMetadata>;
|
|
238
254
|
listRemoteSessions?(input?: { searchTerm?: string; cursor?: string; limit?: number }): Promise<RemoteSessionPage>;
|
|
239
255
|
readRemoteSession?(remoteSessionId: string, view?: "metadata" | "latest" | "latest-full"): Promise<RemoteSessionSummary>;
|
|
256
|
+
readRemoteSessionMetrics?(remoteSessionId: string): Promise<RemoteSessionMetrics>;
|
|
240
257
|
readRemoteForkSource?(remoteSessionId: string): Promise<RemoteSessionSummary>;
|
|
241
258
|
listRemoteTurnSummaries?(remoteSessionId: string, input: { cursor?: string; limit: number }): Promise<RemoteTurnPage>;
|
|
242
259
|
inspectRemoteSessionActivity?(remoteSessionId: string): Promise<RemoteSessionActivity>;
|
|
@@ -261,7 +278,7 @@ export interface AgentRuntime {
|
|
|
261
278
|
persist?: (session: RuntimeSession) => Promise<void>,
|
|
262
279
|
): Promise<RuntimeSession>;
|
|
263
280
|
respondToApproval(sessionId: string, requestId: string, decision: ApprovalDecision): Promise<void>;
|
|
264
|
-
listModels(): Promise<ModelOption[]>;
|
|
281
|
+
listModels(modelProvider?: string, fallbackModel?: string): Promise<ModelOption[]>;
|
|
265
282
|
listModelProviders?(): Promise<ModelProviderOption[]>;
|
|
266
283
|
release?(options?: { force?: boolean }): Promise<RuntimeReleaseResult>;
|
|
267
284
|
onEvent(listener: (event: RuntimeEvent) => void): () => void;
|
package/src/state/StateStore.ts
CHANGED
|
@@ -1332,6 +1332,15 @@ export class StateStore {
|
|
|
1332
1332
|
}));
|
|
1333
1333
|
}
|
|
1334
1334
|
|
|
1335
|
+
countTaskTurns(localSessionId: string): number {
|
|
1336
|
+
const turnIds = new Set(this.listTaskTurnGraphIndex(localSessionId).map((turn) => turn.turnId));
|
|
1337
|
+
const own = this.db.prepare(
|
|
1338
|
+
"SELECT turn_id AS turnId FROM turn_snapshots WHERE local_session_id = ?",
|
|
1339
|
+
).all(localSessionId) as Array<{ turnId: string }>;
|
|
1340
|
+
for (const turn of own) turnIds.add(turn.turnId);
|
|
1341
|
+
return turnIds.size;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1335
1344
|
listTaskTurnGraphIndex(localSessionId: string): CompletedTurnIndexRecord[] {
|
|
1336
1345
|
const backfilled = new Set<string>();
|
|
1337
1346
|
const backfill = (id: string): void => {
|