lark-remote 0.1.6 → 0.1.8
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 +51 -1
- package/README.en.md +9 -1
- package/README.md +14 -2
- package/dist/bridge/approval-coordinator.d.ts +71 -34
- package/dist/bridge/approval-coordinator.js +225 -41
- package/dist/bridge/inbound-media.d.ts +62 -0
- package/dist/bridge/inbound-media.js +282 -0
- package/dist/bridge/index.d.ts +97 -13
- package/dist/bridge/index.js +227 -87
- package/dist/bridge/queue-manager.d.ts +2 -1
- package/dist/bridge/queue-manager.js +1 -0
- package/dist/card/approval-render.js +294 -36
- package/dist/card/bash-card-session.d.ts +2 -24
- package/dist/card/bash-card-session.js +6 -73
- package/dist/card/bash-renderer.js +2 -3
- package/dist/card/card-budget.js +29 -75
- package/dist/card/card-session.d.ts +48 -0
- package/dist/card/card-session.js +86 -0
- package/dist/card/card-shared.d.ts +7 -16
- package/dist/card/card-shared.js +7 -30
- package/dist/card/run-card-session.d.ts +2 -55
- package/dist/card/run-card-session.js +6 -104
- package/dist/card/run-renderer.d.ts +7 -0
- package/dist/card/run-renderer.js +116 -238
- package/dist/card/run-state.d.ts +13 -0
- package/dist/card/run-state.js +25 -16
- package/dist/card/text-truncate.d.ts +13 -12
- package/dist/card/text-truncate.js +13 -63
- package/dist/common/fs.d.ts +8 -0
- package/dist/common/fs.js +17 -0
- package/dist/common/guards.d.ts +0 -20
- package/dist/common/guards.js +0 -43
- package/dist/common/sleep.d.ts +2 -0
- package/dist/common/sleep.js +5 -0
- package/dist/common/truncate.d.ts +39 -0
- package/dist/common/truncate.js +92 -0
- package/dist/config/codex-config.d.ts +1 -1
- package/dist/config/codex-config.js +1 -1
- package/dist/config/index.d.ts +112 -24
- package/dist/config/index.js +139 -37
- package/dist/connector/index.d.ts +69 -2
- package/dist/connector/index.js +107 -1
- package/dist/index.js +150 -118
- package/dist/instance-lock.js +4 -12
- package/dist/order/alias-resolve.d.ts +13 -0
- package/dist/order/alias-resolve.js +22 -0
- package/dist/order/alias-store.d.ts +38 -0
- package/dist/order/alias-store.js +106 -0
- package/dist/persistence/atomic-write.d.ts +8 -2
- package/dist/persistence/atomic-write.js +23 -7
- package/dist/restart.js +2 -1
- package/dist/router/card-action-payload.d.ts +30 -0
- package/dist/router/card-action-payload.js +39 -0
- package/dist/router/card-helpers.d.ts +63 -0
- package/dist/router/card-helpers.js +146 -0
- package/dist/router/config/claude.js +30 -1
- package/dist/router/config/codex.js +29 -50
- package/dist/router/config/common/model-patch.d.ts +22 -0
- package/dist/router/config/common/model-patch.js +26 -0
- package/dist/router/config/common/render.js +5 -4
- package/dist/router/config/dsh.d.ts +51 -0
- package/dist/router/config/dsh.js +179 -0
- package/dist/router/config/index.d.ts +12 -0
- package/dist/router/config/index.js +28 -1
- package/dist/router/config/kimi.d.ts +4 -1
- package/dist/router/config/kimi.js +27 -0
- package/dist/router/config/opencode.js +18 -5
- package/dist/router/config/pi.js +4 -5
- package/dist/router/config/types.d.ts +17 -2
- package/dist/router/index.d.ts +76 -22
- package/dist/router/index.js +479 -548
- package/dist/router/utils.d.ts +2 -0
- package/dist/router/utils.js +16 -2
- package/dist/runner/agent-choices-common.d.ts +16 -0
- package/dist/runner/agent-choices-common.js +34 -0
- package/dist/runner/claude/runner.d.ts +47 -10
- package/dist/runner/claude/runner.js +117 -46
- package/dist/runner/claude/session.d.ts +166 -0
- package/dist/runner/claude/session.js +768 -0
- package/dist/runner/codex/app-server/protocol-types.d.ts +66 -39
- package/dist/runner/codex/app-server/protocol-types.js +2 -13
- package/dist/runner/codex/app-server/runner.d.ts +52 -59
- package/dist/runner/codex/app-server/runner.js +216 -338
- package/dist/runner/codex/app-server/translator.d.ts +8 -21
- package/dist/runner/codex/app-server/translator.js +45 -18
- package/dist/runner/codex/index.d.ts +0 -2
- package/dist/runner/codex/index.js +0 -2
- package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
- package/dist/runner/common/acp/protocol-helpers.js +21 -0
- package/dist/runner/common/acp/protocol-types.d.ts +271 -0
- package/dist/runner/common/acp/protocol-types.js +77 -0
- package/dist/runner/common/connection-based-runner.d.ts +111 -0
- package/dist/runner/common/connection-based-runner.js +261 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
- package/dist/runner/common/jsonrpc/types.d.ts +32 -0
- package/dist/runner/common/jsonrpc/types.js +9 -0
- package/dist/runner/common/process-stopper.d.ts +2 -1
- package/dist/runner/common/process-stopper.js +1 -0
- package/dist/runner/common/runner-utils.d.ts +6 -13
- package/dist/runner/common/runner-utils.js +6 -47
- package/dist/runner/common/spawning-runner.d.ts +24 -70
- package/dist/runner/common/spawning-runner.js +98 -152
- package/dist/runner/dsh/client.d.ts +95 -0
- package/dist/runner/dsh/client.js +418 -0
- package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
- package/dist/runner/dsh/fake-dsh-server.js +156 -0
- package/dist/runner/dsh/index.d.ts +3 -0
- package/dist/runner/dsh/index.js +4 -0
- package/dist/runner/dsh/runner.d.ts +57 -0
- package/dist/runner/dsh/runner.js +191 -0
- package/dist/runner/dsh/translator.d.ts +28 -0
- package/dist/runner/dsh/translator.js +98 -0
- package/dist/runner/dsh/types.d.ts +155 -0
- package/dist/runner/dsh/types.js +14 -0
- package/dist/runner/index.d.ts +5 -4
- package/dist/runner/index.js +5 -4
- package/dist/runner/kimi/acp/runner.d.ts +106 -0
- package/dist/runner/kimi/acp/runner.js +516 -0
- package/dist/runner/kimi/acp/translator.d.ts +138 -0
- package/dist/runner/kimi/acp/translator.js +441 -0
- package/dist/runner/kimi/index.d.ts +1 -1
- package/dist/runner/kimi/index.js +1 -1
- package/dist/runner/opencode/acp/index.d.ts +1 -0
- package/dist/runner/opencode/acp/index.js +2 -0
- package/dist/runner/opencode/acp/runner.d.ts +121 -0
- package/dist/runner/opencode/acp/runner.js +429 -0
- package/dist/runner/opencode/acp/translator.d.ts +119 -0
- package/dist/runner/opencode/acp/translator.js +345 -0
- package/dist/runner/opencode/index.d.ts +1 -1
- package/dist/runner/opencode/index.js +1 -1
- package/dist/runner/pi/index.d.ts +1 -1
- package/dist/runner/pi/index.js +1 -1
- package/dist/runner/pi/rpc/client.d.ts +52 -0
- package/dist/runner/pi/rpc/client.js +114 -0
- package/dist/runner/pi/rpc/index.d.ts +4 -0
- package/dist/runner/pi/rpc/index.js +4 -0
- package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
- package/dist/runner/pi/rpc/protocol-types.js +21 -0
- package/dist/runner/pi/rpc/runner.d.ts +74 -0
- package/dist/runner/pi/rpc/runner.js +190 -0
- package/dist/runner/pi/rpc/translator.d.ts +55 -0
- package/dist/runner/pi/rpc/translator.js +258 -0
- package/dist/runner/probe.js +6 -1
- package/dist/runner/question-common.d.ts +26 -0
- package/dist/runner/question-common.js +42 -0
- package/dist/runner/registry.d.ts +7 -8
- package/dist/runner/registry.js +3 -4
- package/dist/runner/resolve-agent-choices.js +18 -59
- package/dist/runner/sync-agent-choices.js +16 -67
- package/dist/runner/types.d.ts +97 -14
- package/dist/session/claude/session-index.js +2 -3
- package/dist/session/claude/session-reader.js +3 -6
- package/dist/session/claude/sessions.js +43 -28
- package/dist/session/codex/rollout-reader.d.ts +2 -2
- package/dist/session/codex/rollout-reader.js +14 -17
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -2
- package/dist/session/common/content-blocks.d.ts +7 -0
- package/dist/session/common/content-blocks.js +13 -9
- package/dist/session/common/jsonl.d.ts +8 -25
- package/dist/session/common/jsonl.js +126 -261
- package/dist/session/common/pagination.d.ts +28 -0
- package/dist/session/common/pagination.js +33 -0
- package/dist/session/dsh/index.d.ts +1 -0
- package/dist/session/dsh/index.js +2 -0
- package/dist/session/dsh/sessions.d.ts +39 -0
- package/dist/session/dsh/sessions.js +237 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/kimi/sessions.d.ts +34 -1
- package/dist/session/kimi/sessions.js +208 -98
- package/dist/session/opencode/sessions.d.ts +17 -3
- package/dist/session/opencode/sessions.js +29 -29
- package/dist/session/pi/sessions.d.ts +5 -1
- package/dist/session/pi/sessions.js +12 -16
- package/dist/session/session-store.d.ts +11 -1
- package/dist/session/session-store.js +75 -178
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/install.d.ts +20 -2
- package/dist/update/install.js +51 -7
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.js +8 -1
- package/package.json +2 -1
- package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
- package/dist/runner/codex/argv.d.ts +0 -33
- package/dist/runner/codex/argv.js +0 -40
- package/dist/runner/codex/factory.d.ts +0 -29
- package/dist/runner/codex/factory.js +0 -39
- package/dist/runner/codex/jsonl.d.ts +0 -37
- package/dist/runner/codex/jsonl.js +0 -221
- package/dist/runner/codex/runner.d.ts +0 -66
- package/dist/runner/codex/runner.js +0 -129
- package/dist/runner/common/exec-translator.d.ts +0 -73
- package/dist/runner/common/exec-translator.js +0 -105
- package/dist/runner/kimi/jsonl.d.ts +0 -28
- package/dist/runner/kimi/jsonl.js +0 -143
- package/dist/runner/kimi/runner.d.ts +0 -49
- package/dist/runner/kimi/runner.js +0 -105
- package/dist/runner/opencode/argv.d.ts +0 -32
- package/dist/runner/opencode/argv.js +0 -35
- package/dist/runner/opencode/jsonl.d.ts +0 -68
- package/dist/runner/opencode/jsonl.js +0 -295
- package/dist/runner/opencode/runner.d.ts +0 -61
- package/dist/runner/opencode/runner.js +0 -93
- package/dist/runner/pi/jsonl.d.ts +0 -128
- package/dist/runner/pi/jsonl.js +0 -249
- package/dist/runner/pi/runner.d.ts +0 -36
- package/dist/runner/pi/runner.js +0 -76
- package/dist/session/common/catch-up.d.ts +0 -12
- package/dist/session/common/catch-up.js +0 -31
package/dist/bridge/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { sleep } from '../common/sleep.js';
|
|
2
3
|
import { getLogger } from '../logger/index.js';
|
|
3
4
|
import { RunCardSession } from '../card/run-card-session.js';
|
|
4
5
|
import { renderRunCard } from '../card/run-renderer.js';
|
|
5
6
|
import { renderBashCard } from '../card/bash-renderer.js';
|
|
6
7
|
import { BashCardSession, capBashOutput } from '../card/bash-card-session.js';
|
|
7
|
-
import {
|
|
8
|
-
import { sessionEventPanel } from '../router/card-helpers.js';
|
|
9
|
-
import { formatUsageStats } from '../router/index.js';
|
|
8
|
+
import { buildSessionHistoryCard } from '../router/card-helpers.js';
|
|
10
9
|
import { agentDisplayName, resumeUseButton } from '../card/card-shared.js';
|
|
11
10
|
import { enforceCardBudget } from '../card/card-budget.js';
|
|
12
11
|
import { normalizeResultUsage } from '../runner/common/usage.js';
|
|
13
12
|
import { BashProcessRunner } from '../runner/index.js';
|
|
14
13
|
import { QueueManager, } from './queue-manager.js';
|
|
15
14
|
import { ApprovalCoordinator, decisionToApprovalAction } from './approval-coordinator.js';
|
|
15
|
+
import { InboundMediaHandler } from './inbound-media.js';
|
|
16
16
|
/**
|
|
17
17
|
* Max events to read for the completion notification card (mirror router's
|
|
18
18
|
* AUTO_RESUME_MAX_EVENTS = 5). Without a cap, readSessionContent returns every
|
|
@@ -22,6 +22,12 @@ import { ApprovalCoordinator, decisionToApprovalAction } from './approval-coordi
|
|
|
22
22
|
const COMPLETION_NOTIFICATION_MAX_EVENTS = 5;
|
|
23
23
|
/** Approval request timeout (ms) — pending approval expires after this. */
|
|
24
24
|
const APPROVAL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
25
|
+
/**
|
|
26
|
+
* runner 缓存的工作区数量上限(review P3-3):超过后创建新工作区槽位时
|
|
27
|
+
* 顺带回收「非 active 且进程已停」的死缓存,防止随访问过的工作区数量
|
|
28
|
+
* 无界增长。活进程/在途 run 一律保留(workspace-lifetime 长驻连接不能误杀)。
|
|
29
|
+
*/
|
|
30
|
+
const MAX_CACHED_RUNNER_WORKSPACES = 10;
|
|
25
31
|
/**
|
|
26
32
|
* Max idle time (no stdout events) before a claude run is considered hung
|
|
27
33
|
* (§9.12). When this fires the bridge calls `runner.stop()` to unblock the
|
|
@@ -61,7 +67,9 @@ function terminalReactionEmoji(terminal) {
|
|
|
61
67
|
* Runner/BridgeChannel seams.
|
|
62
68
|
*/
|
|
63
69
|
export class Bridge {
|
|
70
|
+
/** 飞书连接通道(public:集成测试的 seam,替代 as unknown as 访问)。 */
|
|
64
71
|
connector;
|
|
72
|
+
/** 会话存储(public:测试辅助读取/写入 cwd 等,替代私有访问)。 */
|
|
65
73
|
sessionStore;
|
|
66
74
|
config;
|
|
67
75
|
workspaceStore;
|
|
@@ -70,6 +78,8 @@ export class Bridge {
|
|
|
70
78
|
agentRegistry;
|
|
71
79
|
/** Session reader registry for usage and completion cards. */
|
|
72
80
|
sessionReaderRegistry;
|
|
81
|
+
/** 入站媒体(图片/文件)落盘 + 合批提示。 */
|
|
82
|
+
mediaHandler;
|
|
73
83
|
/**
|
|
74
84
|
* Per-workspace runner instances, keyed by (cwd, agentKind).
|
|
75
85
|
* Fix 4 (2026-07-18): Each agentKind has its own cache slot, so switching
|
|
@@ -80,14 +90,15 @@ export class Bridge {
|
|
|
80
90
|
*/
|
|
81
91
|
runners = new Map();
|
|
82
92
|
/** Queue manager for per-workspace serial processing queue. */
|
|
93
|
+
/** 普通消息队列管理器(public:测试注入 queue-card 状态用)。 */
|
|
83
94
|
queueManager;
|
|
84
95
|
/** Active runs per workspace (cwd). Multiple workspaces can run in parallel. */
|
|
85
96
|
activeRuns = new Map();
|
|
86
97
|
/** Approval coordinators keyed by runId. */
|
|
87
98
|
approvalCoordinators = new Map();
|
|
88
99
|
/**
|
|
89
|
-
*
|
|
90
|
-
* 已清空,Compact 需要它来校验 runId 并提供 sessionId。
|
|
100
|
+
* 最近一次已完成的、runner 有 runCompact 的 run(按 cwd)。run 结束后
|
|
101
|
+
* activeRuns 已清空,Compact 需要它来校验 runId 并提供 sessionId。
|
|
91
102
|
*/
|
|
92
103
|
lastCompactableCodexRun = new Map();
|
|
93
104
|
/**
|
|
@@ -106,6 +117,11 @@ export class Bridge {
|
|
|
106
117
|
this.agentRegistry = deps.agentRegistry;
|
|
107
118
|
this.sessionReaderRegistry = deps.sessionReaderRegistry;
|
|
108
119
|
this.queueManager = new QueueManager((workspace) => this.activeRuns.has(workspace), (chatId, card, opts) => this.connector.sendWithRetry(chatId, { card }, { replyTo: opts?.replyTo }), (messageId, card) => this.connector.updateCard(messageId, card));
|
|
120
|
+
this.mediaHandler = new InboundMediaHandler({
|
|
121
|
+
resolveCwd: (userId) => this.resolveCwd(userId),
|
|
122
|
+
getConfig: () => this.config,
|
|
123
|
+
send: async (ctx, text) => this.sendResult({ text }, ctx),
|
|
124
|
+
});
|
|
109
125
|
}
|
|
110
126
|
/**
|
|
111
127
|
* Get or create a runner for the given workspace, keyed by current defaultAgent.
|
|
@@ -120,6 +136,20 @@ export class Bridge {
|
|
|
120
136
|
if (workspaceMap?.has(kind)) {
|
|
121
137
|
return workspaceMap.get(kind);
|
|
122
138
|
}
|
|
139
|
+
// review P3-3:缓存超过上限且是新工作区时才回收死槽位——刚创建未 run
|
|
140
|
+
// 的 runner 同样是 isRunning=false,小缓存下立即回收会破坏「创建即注册」
|
|
141
|
+
// 的既有语义(exit-dispatcher 计数回归)。
|
|
142
|
+
if (this.runners.size >= MAX_CACHED_RUNNER_WORKSPACES && !this.runners.has(workspace)) {
|
|
143
|
+
for (const [cwd, slotMap] of [...this.runners.entries()]) {
|
|
144
|
+
if (this.activeRuns.has(cwd))
|
|
145
|
+
continue;
|
|
146
|
+
for (const [slotKind, r] of [...slotMap.entries()]) {
|
|
147
|
+
if (!r.isRunning) {
|
|
148
|
+
this.evictRunnerSlot(cwd, slotKind);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
123
153
|
// Create new runner for this (workspace, kind) slot
|
|
124
154
|
const runner = this.agentRegistry.get(kind, workspace);
|
|
125
155
|
// Call lifecycle methods for agent runners created via factory
|
|
@@ -147,10 +177,69 @@ export class Bridge {
|
|
|
147
177
|
container.current = config;
|
|
148
178
|
}
|
|
149
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Push updated approval-mode settings to active workspace-lifetime runners.
|
|
182
|
+
* `clearRunners()` deliberately skips workspaces with active runs; this
|
|
183
|
+
* gives those runners a chance to apply the new settings immediately via
|
|
184
|
+
* the unified duck method `updateApprovalMode` (codex → thread/settings/
|
|
185
|
+
* update, kimi → session/set_mode, opencode → session/set_mode) instead of
|
|
186
|
+
* keeping a stale snapshot until the runner is evicted/recreated.
|
|
187
|
+
*/
|
|
188
|
+
syncActiveApprovalModes() {
|
|
189
|
+
for (const [cwd, active] of this.activeRuns) {
|
|
190
|
+
const runner = active.runner;
|
|
191
|
+
if (typeof runner.updateApprovalMode !== 'function')
|
|
192
|
+
continue;
|
|
193
|
+
const settings = this.approvalModeSettingsFor(active.agentKind);
|
|
194
|
+
if (!settings)
|
|
195
|
+
continue;
|
|
196
|
+
void runner.updateApprovalMode(settings).catch((err) => {
|
|
197
|
+
getLogger().warn(`[bridge] sync approval mode failed cwd=${cwd}: ${err.message}`);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** Per-agent approval-mode settings snapshot from the current config. */
|
|
202
|
+
approvalModeSettingsFor(kind) {
|
|
203
|
+
const agents = this.config.agents;
|
|
204
|
+
switch (kind) {
|
|
205
|
+
case 'codex': {
|
|
206
|
+
const codexConfig = agents?.codex;
|
|
207
|
+
if (!codexConfig)
|
|
208
|
+
return undefined;
|
|
209
|
+
return {
|
|
210
|
+
approvalPolicy: codexConfig.approvalPolicy,
|
|
211
|
+
sandbox: codexConfig.sandbox,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
case 'kimi': {
|
|
215
|
+
const kimiConfig = agents?.kimi;
|
|
216
|
+
if (!kimiConfig)
|
|
217
|
+
return undefined;
|
|
218
|
+
return { permissionMode: kimiConfig.permissionMode };
|
|
219
|
+
}
|
|
220
|
+
case 'opencode': {
|
|
221
|
+
const opencodeConfig = agents?.opencode;
|
|
222
|
+
if (!opencodeConfig)
|
|
223
|
+
return undefined;
|
|
224
|
+
return { mode: opencodeConfig.mode };
|
|
225
|
+
}
|
|
226
|
+
default:
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
150
230
|
/** Get the current runner for a workspace. Used by router for agent-specific commands (P3.2). */
|
|
151
231
|
getCurrentRunner(workspace) {
|
|
152
232
|
return this.getRunner(workspace);
|
|
153
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Whether the runner for the given agent kind has runCompact capability.
|
|
236
|
+
* Used by router for compact button gating (design doc §6.2-2: duck-typing
|
|
237
|
+
* instead of hardcoding agentKind === 'codex').
|
|
238
|
+
*/
|
|
239
|
+
hasRunCompact(workspace, kind = this.config.defaultAgent) {
|
|
240
|
+
const runner = this.getRunner(workspace, kind);
|
|
241
|
+
return ('runCompact' in runner && typeof runner.runCompact === 'function');
|
|
242
|
+
}
|
|
154
243
|
/** 入队时刻快照:当前 defaultAgent + 该 agent 的 sessionId(无 session 则 undefined)。
|
|
155
244
|
* 唯一捕获点:排队消息在 T0 把 agent+session 钉进 AgentBinding,随任务闭包带到 T1
|
|
156
245
|
* 执行时刻,避免 /new、/config 在排队期间改写 live 状态导致语义漂移(方案 D4)。 */
|
|
@@ -216,6 +305,7 @@ export class Bridge {
|
|
|
216
305
|
* Extract agent-specific run options from config.
|
|
217
306
|
* Returns model/effort/etc. based on current defaultAgent.
|
|
218
307
|
*/
|
|
308
|
+
/** 解析某 agent 的 run 选项(forwardToClaude 用;测试直接调用的 seam)。 */
|
|
219
309
|
getAgentRunOptions(agent = this.config.defaultAgent) {
|
|
220
310
|
const agents = this.config.agents;
|
|
221
311
|
const result = {};
|
|
@@ -421,7 +511,10 @@ export class Bridge {
|
|
|
421
511
|
// its slot interrupted, hiding it from queue cards (A22).
|
|
422
512
|
const stoppedSlot = this.queueManager.getExecutingSlot(cwd);
|
|
423
513
|
await Promise.allSettled([
|
|
424
|
-
|
|
514
|
+
// 用户主动中断(/stop、stop 卡按钮、queue.immediate)显式打标
|
|
515
|
+
// interruptedReason,与审批超时自动取消(approval_timeout)、审批
|
|
516
|
+
// 卡取消(approval_cancelled)在终态数据上可区分(方案 A)。
|
|
517
|
+
active.session.finish('interrupted', { interruptedReason: 'user_stop' }),
|
|
425
518
|
runner.stop({ immediate: true }),
|
|
426
519
|
]);
|
|
427
520
|
// The chain may have advanced while the stop was in flight: a new run
|
|
@@ -554,6 +647,18 @@ export class Bridge {
|
|
|
554
647
|
await this.sendResult({ text: `发送文件失败: ${err.message}` }, ctx);
|
|
555
648
|
}
|
|
556
649
|
}
|
|
650
|
+
/** 入站媒体落盘入口(connector 下载完成后调用)。 */
|
|
651
|
+
async onInboundMedia(payload) {
|
|
652
|
+
await this.mediaHandler.handle(payload);
|
|
653
|
+
}
|
|
654
|
+
/** 文本消息到达时立即冲刷待合批的媒体保存提示。 */
|
|
655
|
+
flushMediaNotifications(userId, chatId) {
|
|
656
|
+
this.mediaHandler.flushAll(userId, chatId);
|
|
657
|
+
}
|
|
658
|
+
/** 冲刷全部待合批提示(/exit、/restart 干净退出前调用)。 */
|
|
659
|
+
async flushAllMediaNotifications() {
|
|
660
|
+
await this.mediaHandler.flushAllPending();
|
|
661
|
+
}
|
|
557
662
|
/** Reconnect the underlying Feishu channel. */
|
|
558
663
|
async reconnect() {
|
|
559
664
|
await this.connector.reconnect();
|
|
@@ -648,6 +753,9 @@ export class Bridge {
|
|
|
648
753
|
showToolResult: this.config.output.showToolResult,
|
|
649
754
|
// Run card header shows the current agent name (e.g. "Claude · 思考中")
|
|
650
755
|
agentKind,
|
|
756
|
+
// Compact 按钮能力门控与 resume 卡 / auto-resume 卡一致(hasRunCompact
|
|
757
|
+
// 鸭子探测):claude(stream-json,无 turn_started)也能拿到按钮。
|
|
758
|
+
compactSupported: this.hasRunCompact(cwd, agentKind),
|
|
651
759
|
},
|
|
652
760
|
});
|
|
653
761
|
const activeRun = {
|
|
@@ -668,9 +776,8 @@ export class Bridge {
|
|
|
668
776
|
* Handles idle watchdog, event processing, session sync, and state transitions.
|
|
669
777
|
*/
|
|
670
778
|
async runAgentStreamToEnd(runner, cardSession, sessionId, sessionEpochAtStart, ctx, cwd, runId, message, agentKind = this.config.defaultAgent) {
|
|
671
|
-
// Runner 声明的 usage 权威来源(review P3-7):codex
|
|
672
|
-
//
|
|
673
|
-
// 其余 runner 未声明,回退到 agentKind 推断(仅 codex 视为 jsonl-first)。
|
|
779
|
+
// Runner 声明的 usage 权威来源(review P3-7):codex app-server = 'live'
|
|
780
|
+
// (turn/started 的 tokenUsage.last 是本 turn 增量)。其余 runner 未声明。
|
|
674
781
|
const usageAuthority = runner.getUsageAuthority?.();
|
|
675
782
|
// P2-1: idle watchdog 用单 interval + lastEventTs 截止时间判定,而非每事件
|
|
676
783
|
// clearTimeout + setTimeout 重建 timer。高频事件流下 timer 重建次数与事件数
|
|
@@ -732,6 +839,15 @@ export class Bridge {
|
|
|
732
839
|
// "15min 无事件即 stop" 的语义不变(§9.12)。周期取 idleTimeoutMs/2,使停滞
|
|
733
840
|
// 后最多在 1.5× idleTimeoutMs 内触发(半个窗口延迟,watchdog 语义可接受)。
|
|
734
841
|
const fireIdleTimeout = () => {
|
|
842
|
+
// 权限等待期间暂停空闲看门狗:claude/codex 等待人工决策时无 stdout
|
|
843
|
+
// 事件,「等审批」不是「挂死」。审批由 ApprovalCoordinator 的超时
|
|
844
|
+
// (默认 5 分钟 < 看门狗窗口)自愈:cancel 送达 + 中断 turn,不会无限
|
|
845
|
+
// 挂起。
|
|
846
|
+
const pendingApproval = this.approvalCoordinators.get(runId)?.pendingCount() ?? 0;
|
|
847
|
+
if (pendingApproval > 0) {
|
|
848
|
+
lastEventTs = Date.now();
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
735
851
|
if (idleInterval)
|
|
736
852
|
clearInterval(idleInterval);
|
|
737
853
|
idleInterval = null;
|
|
@@ -861,8 +977,8 @@ export class Bridge {
|
|
|
861
977
|
// app-server 模式 setup 失败路径(thread/start 成功后 turn/start 失败)
|
|
862
978
|
// 不会有 turn_started 事件,store 不会写回新线程 id;runner 已在 result
|
|
863
979
|
// 里兜底上报(review P3-10)。此处仅当 runner 声明 live usage(app-server)
|
|
864
|
-
//
|
|
865
|
-
//
|
|
980
|
+
// 且会话指针未动时写回,避免下条消息再开一个孤儿线程。其余 agent 不在
|
|
981
|
+
// 此列(它们各自有 system.init / turn_started 写回)。
|
|
866
982
|
if (usageAuthority === 'live' &&
|
|
867
983
|
event.session_id &&
|
|
868
984
|
this.sessionStore.getSessionEpoch(ctx.userId, agentKind) === sessionEpochAtStart) {
|
|
@@ -918,11 +1034,11 @@ export class Bridge {
|
|
|
918
1034
|
let coordinator = this.approvalCoordinators.get(runId);
|
|
919
1035
|
if (!coordinator) {
|
|
920
1036
|
coordinator = new ApprovalCoordinator({
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1037
|
+
// claude 审批超时走 config(claude.approvalTimeoutMs,默认 5 分钟,
|
|
1038
|
+
// 对齐 codex 红线勿改短);codex 维持模块常量。
|
|
1039
|
+
approvalTimeoutMs: agentKind === 'claude'
|
|
1040
|
+
? (this.config.claude?.approvalTimeoutMs ?? APPROVAL_TIMEOUT_MS)
|
|
1041
|
+
: APPROVAL_TIMEOUT_MS,
|
|
926
1042
|
responder: async (requestId, response) => {
|
|
927
1043
|
const r = runner;
|
|
928
1044
|
if (typeof r.respondApproval === 'function') {
|
|
@@ -982,17 +1098,13 @@ export class Bridge {
|
|
|
982
1098
|
finalCumulativeOutputTokens = finalUsage?.cumulativeOutputTokens;
|
|
983
1099
|
finalCumulativeCacheReadTokens = finalUsage?.cumulativeCacheReadTokens;
|
|
984
1100
|
finalCumulativeCacheCreationTokens = finalUsage?.cumulativeCacheCreationTokens;
|
|
985
|
-
// 统一 flow 字段 scope:live
|
|
1101
|
+
// 统一 flow 字段 scope:live 优先,jsonl 兜底。
|
|
986
1102
|
// 当 live result 事件带了 input/output(即 liveInputTokens/liveOutputTokens
|
|
987
1103
|
// 非 undefined),所有 flow 字段统一用 live scope(本 run),不用 jsonl
|
|
988
1104
|
// 的 session 累计值覆盖,避免 cache%/Total 分子分母不同源。
|
|
989
1105
|
// 无 live usage 时(如 kimi),jsonl 值兜底。
|
|
990
|
-
// codex 例外(2026-08-01):codex exec 的 turn.completed.usage 是会话累计
|
|
991
|
-
// total_token_usage(resume 长会话下虚高数百倍),不是本 run 增量——live
|
|
992
|
-
// 值必须被忽略,flow 字段一律取 jsonl 主线程文件的 per-turn 值;
|
|
993
|
-
// jsonl 缺失(老版本无 token_count)时才回退 live。
|
|
994
1106
|
// contextLength/compactCount 保持 jsonl 优先(它们是水位/历史计数)。
|
|
995
|
-
const codexJsonlFirst = usageAuthority === 'jsonl'
|
|
1107
|
+
const codexJsonlFirst = usageAuthority === 'jsonl';
|
|
996
1108
|
if (!codexJsonlFirst && liveInputTokens !== undefined && liveOutputTokens !== undefined) {
|
|
997
1109
|
// 非 codex 且 live 有 input/output → 所有 flow 字段用 live scope
|
|
998
1110
|
finalInputTokens = liveInputTokens;
|
|
@@ -1002,7 +1114,10 @@ export class Bridge {
|
|
|
1002
1114
|
// 由 formatUsageStats 的 max/兜底公式处理。
|
|
1003
1115
|
}
|
|
1004
1116
|
else {
|
|
1005
|
-
// codex(jsonl 优先)或无 live usage → jsonl 兜底所有 flow
|
|
1117
|
+
// codex(jsonl 优先)或无 live usage → jsonl 兜底所有 flow 字段。
|
|
1118
|
+
// jsonl 的非累计字段已是末轮(本 run)scope(claude reader 修复后对齐
|
|
1119
|
+
// codex `last_token_usage` 语义,见 scalarScan),不再是 session 累计;
|
|
1120
|
+
// session 累计只经 cumulative* 字段透传,卡片"本 run ≤ 累计"不变量成立。
|
|
1006
1121
|
finalInputTokens = finalUsage?.inputTokens ?? liveInputTokens;
|
|
1007
1122
|
finalOutputTokens = finalUsage?.outputTokens ?? liveOutputTokens;
|
|
1008
1123
|
finalCacheReadTokens = finalUsage?.cacheReadTokens ?? finalCacheReadTokens;
|
|
@@ -1193,15 +1308,19 @@ export class Bridge {
|
|
|
1193
1308
|
// Compact 卡片需要 runId + sessionId;record 供终态卡上的 Compact 按钮
|
|
1194
1309
|
// 校验与取参(activeRuns 已清空,无法再查到)。异常退出(error/
|
|
1195
1310
|
// interrupted/idle_timeout)同样记录——上下文往往更大,压缩后再续。
|
|
1311
|
+
// §6.2-3:写入条件从「codex 终态」放宽为「runner 有 runCompact 的终态」
|
|
1312
|
+
// (codex app-server + kimi acp 均满足)。
|
|
1196
1313
|
const runTerminal = cardSession.currentState.terminal;
|
|
1197
|
-
|
|
1314
|
+
const runnerHasCompact = 'runCompact' in activeRun.runner &&
|
|
1315
|
+
typeof activeRun.runner.runCompact === 'function';
|
|
1316
|
+
if (runnerHasCompact &&
|
|
1198
1317
|
activeRun.runner.lifetime === 'workspace' &&
|
|
1199
1318
|
(runTerminal === 'done' ||
|
|
1200
1319
|
runTerminal === 'error' ||
|
|
1201
1320
|
runTerminal === 'interrupted' ||
|
|
1202
1321
|
runTerminal === 'idle_timeout')) {
|
|
1203
1322
|
const compactSessionId = cardSession.currentState.sessionId ??
|
|
1204
|
-
this.sessionStore.getSessionId(ctx.userId,
|
|
1323
|
+
this.sessionStore.getSessionId(ctx.userId, activeRun.agentKind);
|
|
1205
1324
|
if (compactSessionId) {
|
|
1206
1325
|
this.lastCompactableCodexRun.set(cwd, {
|
|
1207
1326
|
runId: activeRun.runId,
|
|
@@ -1270,42 +1389,19 @@ export class Bridge {
|
|
|
1270
1389
|
maxEvents: COMPLETION_NOTIFICATION_MAX_EVENTS,
|
|
1271
1390
|
});
|
|
1272
1391
|
const { events, usage } = content;
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
// Add action button to resume this session - use column_set+column for 2.0
|
|
1287
|
-
elements.push({
|
|
1288
|
-
tag: 'column_set',
|
|
1289
|
-
columns: [
|
|
1290
|
-
{
|
|
1291
|
-
tag: 'column',
|
|
1292
|
-
width: 'auto',
|
|
1293
|
-
elements: [resumeUseButton(sessionId, agentKind)],
|
|
1294
|
-
},
|
|
1295
|
-
],
|
|
1392
|
+
const card = buildSessionHistoryCard({
|
|
1393
|
+
sessionId,
|
|
1394
|
+
cwd,
|
|
1395
|
+
events,
|
|
1396
|
+
usage,
|
|
1397
|
+
}, {
|
|
1398
|
+
agentKind,
|
|
1399
|
+
headerText: `📂 \`${cwd}\`\n会话: **${sessionId}**`,
|
|
1400
|
+
title: '✅ 会话已完成',
|
|
1401
|
+
usageResult: 'completed',
|
|
1402
|
+
actions: [resumeUseButton(sessionId, agentKind)],
|
|
1403
|
+
headerTemplate: 'green',
|
|
1296
1404
|
});
|
|
1297
|
-
const card = {
|
|
1298
|
-
schema: '2.0',
|
|
1299
|
-
config: { wide_screen_mode: true },
|
|
1300
|
-
header: {
|
|
1301
|
-
template: 'green',
|
|
1302
|
-
title: {
|
|
1303
|
-
tag: 'plain_text',
|
|
1304
|
-
content: '✅ 会话已完成',
|
|
1305
|
-
},
|
|
1306
|
-
},
|
|
1307
|
-
body: { elements },
|
|
1308
|
-
};
|
|
1309
1405
|
// P1-21: send through sendResult so enforceCardBudget guards the card
|
|
1310
1406
|
// (previously sent via connector directly, bypassing the budget — long
|
|
1311
1407
|
// sessions produced >28KB cards that Feishu silently rejected).
|
|
@@ -1325,22 +1421,31 @@ export class Bridge {
|
|
|
1325
1421
|
// Approval action handlers
|
|
1326
1422
|
// =============================================================================
|
|
1327
1423
|
/**
|
|
1328
|
-
*
|
|
1329
|
-
*
|
|
1424
|
+
* Resolve the ApprovalCoordinator for a run, or throw the standard
|
|
1425
|
+
* "已结束" error. Shared preamble of all approval card-action handlers.
|
|
1330
1426
|
*/
|
|
1331
|
-
|
|
1332
|
-
const
|
|
1333
|
-
log.info(`[bridge] handleApprovalRespond runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} decision=${opts.decision}`);
|
|
1334
|
-
const coordinator = this.approvalCoordinators.get(opts.runId);
|
|
1427
|
+
withCoordinator(runId, fn) {
|
|
1428
|
+
const coordinator = this.approvalCoordinators.get(runId);
|
|
1335
1429
|
if (!coordinator) {
|
|
1336
1430
|
// P2-7: run 已结束(coordinator 已释放)时点击审批按钮必须给用户明确反馈,
|
|
1337
1431
|
// 不能静默。router 会把该异常转成 error toast。
|
|
1338
|
-
|
|
1432
|
+
getLogger().warn(`[bridge] no approval coordinator for runId=${runId.slice(0, 8)}`);
|
|
1339
1433
|
throw new Error('任务已结束,审批无法响应');
|
|
1340
1434
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1435
|
+
return fn(coordinator);
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Handle an approval response from a card button click.
|
|
1439
|
+
* Routes to the ApprovalCoordinator for the given run.
|
|
1440
|
+
*/
|
|
1441
|
+
async handleApprovalRespond(opts) {
|
|
1442
|
+
const log = getLogger();
|
|
1443
|
+
log.info(`[bridge] handleApprovalRespond runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} decision=${opts.decision}`);
|
|
1444
|
+
return this.withCoordinator(opts.runId, async (coordinator) => {
|
|
1445
|
+
// Build ApprovalAction from the decision string
|
|
1446
|
+
const action = decisionToApprovalAction(opts.decision);
|
|
1447
|
+
await coordinator.submit(action, { requestId: opts.requestId, nonce: opts.nonce });
|
|
1448
|
+
});
|
|
1344
1449
|
}
|
|
1345
1450
|
/**
|
|
1346
1451
|
* Handle a permission toggle from a card button click.
|
|
@@ -1349,22 +1454,57 @@ export class Bridge {
|
|
|
1349
1454
|
async handleApprovalToggle(opts) {
|
|
1350
1455
|
const log = getLogger();
|
|
1351
1456
|
log.info(`[bridge] handleApprovalToggle runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} permId=${opts.permId}`);
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1457
|
+
return this.withCoordinator(opts.runId, async (coordinator) => {
|
|
1458
|
+
// selected 由卡片根据当前渲染状态传回(取反),不再硬编码只授不撤。
|
|
1459
|
+
const toggleAction = {
|
|
1460
|
+
permId: opts.permId,
|
|
1461
|
+
selected: opts.selected ?? true,
|
|
1462
|
+
};
|
|
1463
|
+
await coordinator.togglePerm(toggleAction, { requestId: opts.requestId });
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Claude AskUserQuestion 选项点击(单选即时提交 / 多选切换勾选)。
|
|
1468
|
+
* 路由到 ApprovalCoordinator.toggleAnswer。
|
|
1469
|
+
*/
|
|
1470
|
+
async handleApprovalAnswer(opts) {
|
|
1471
|
+
const log = getLogger();
|
|
1472
|
+
log.info(`[bridge] handleApprovalAnswer runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1473
|
+
return this.withCoordinator(opts.runId, (coordinator) => coordinator.toggleAnswer({ questionIndex: opts.questionIndex, option: opts.option }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Claude AskUserQuestion 多选问题的「提交答案」按钮。
|
|
1477
|
+
* 路由到 ApprovalCoordinator.submitAnswers。
|
|
1478
|
+
*/
|
|
1479
|
+
async handleApprovalAnswerSubmit(opts) {
|
|
1480
|
+
const log = getLogger();
|
|
1481
|
+
log.info(`[bridge] handleApprovalAnswerSubmit runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1482
|
+
return this.withCoordinator(opts.runId, (coordinator) => coordinator.submitAnswers({ questionIndex: opts.questionIndex }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Claude AskUserQuestion 自定义答案(Other,review P3-4):自由文本直接
|
|
1486
|
+
* 作为该单选问题的答案提交。路由到 ApprovalCoordinator.answerCustom。
|
|
1487
|
+
*/
|
|
1488
|
+
async handleApprovalAnswerCustom(opts) {
|
|
1489
|
+
const log = getLogger();
|
|
1490
|
+
log.info(`[bridge] handleApprovalAnswerCustom runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1491
|
+
return this.withCoordinator(opts.runId, (coordinator) => coordinator.answerCustom({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Codex AskUserQuestion 补充说明(user_note,2026-08-18 live 验证模型理解
|
|
1495
|
+
* "user_note: <text>" 条目):记录到 coordinator,随答案一起提交。
|
|
1496
|
+
* 路由到 ApprovalCoordinator.answerNote。
|
|
1497
|
+
*/
|
|
1498
|
+
async handleApprovalAnswerNote(opts) {
|
|
1499
|
+
const log = getLogger();
|
|
1500
|
+
log.info(`[bridge] handleApprovalAnswerNote runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1501
|
+
return this.withCoordinator(opts.runId, (coordinator) => coordinator.answerNote({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1363
1502
|
}
|
|
1364
1503
|
/**
|
|
1365
|
-
* Handle codex.compact card action — trigger a
|
|
1366
|
-
*
|
|
1367
|
-
* the
|
|
1504
|
+
* Handle codex.compact card action — trigger a compaction request for any
|
|
1505
|
+
* runCompact-capable runner(codex/kimi/opencode/pi/claude,按
|
|
1506
|
+
* lastCompactableCodexRun 记录的 agentKind 路由)。Validates the run exists
|
|
1507
|
+
* and is in a terminal state, then calls the runner's runCompact().
|
|
1368
1508
|
*/
|
|
1369
1509
|
async handleCodexCompact(value, ctx) {
|
|
1370
1510
|
const log = getLogger();
|
|
@@ -1406,7 +1546,7 @@ export class Bridge {
|
|
|
1406
1546
|
});
|
|
1407
1547
|
}
|
|
1408
1548
|
/**
|
|
1409
|
-
* Stream a
|
|
1549
|
+
* Stream a compaction to a card: start a RunCardSession with
|
|
1410
1550
|
* operationKind='compaction' (no recursive Compact button), consume
|
|
1411
1551
|
* runner.runCompact() events, read authoritative jsonl usage, and finish
|
|
1412
1552
|
* the card. Shared by handleCodexCompact (run card button) and
|
|
@@ -1460,7 +1600,7 @@ export class Bridge {
|
|
|
1460
1600
|
// (实测差 ~9ms),compactCount 未读到则短重试,仍失败优雅降级(无统计)。
|
|
1461
1601
|
let finalUsage = this.resolveFinalUsage(sessionId, cwd, agentKind);
|
|
1462
1602
|
for (let attempt = 0; !finalUsage?.compactCount && attempt < 2; attempt++) {
|
|
1463
|
-
await
|
|
1603
|
+
await sleep(150);
|
|
1464
1604
|
finalUsage = this.resolveFinalUsage(sessionId, cwd, agentKind);
|
|
1465
1605
|
}
|
|
1466
1606
|
await cardSession.finish(sawResult ? 'done' : 'error', {
|
|
@@ -1536,7 +1676,7 @@ export class Bridge {
|
|
|
1536
1676
|
await this.sendResult({ text: `⚠️ 未找到 session ${sessionId}(当前目录: ${cwd})` }, ctx);
|
|
1537
1677
|
return;
|
|
1538
1678
|
}
|
|
1539
|
-
// Check that the runner has runCompact
|
|
1679
|
+
// Check that the runner has runCompact(codex/kimi/opencode/pi/claude 鸭子探测)。
|
|
1540
1680
|
const runner = this.getRunner(cwd, agentKind);
|
|
1541
1681
|
if (!('runCompact' in runner) ||
|
|
1542
1682
|
typeof runner.runCompact !== 'function') {
|
|
@@ -101,7 +101,8 @@ export declare class QueueManager {
|
|
|
101
101
|
* executing/cancelled state. The promise never rejects (send failures
|
|
102
102
|
* resolve undefined), so awaiting it is always safe.
|
|
103
103
|
*/
|
|
104
|
-
|
|
104
|
+
/** queue card 的 send promise 表(public:bridge 集成测试注入/断言用)。 */
|
|
105
|
+
queueCardMessages: Map<string, Promise<string | undefined>>;
|
|
105
106
|
/** Track number of executing tasks per workspace (for queue card display). */
|
|
106
107
|
private pendingOrExecutingCount;
|
|
107
108
|
/**
|
|
@@ -38,6 +38,7 @@ export class QueueManager {
|
|
|
38
38
|
* executing/cancelled state. The promise never rejects (send failures
|
|
39
39
|
* resolve undefined), so awaiting it is always safe.
|
|
40
40
|
*/
|
|
41
|
+
/** queue card 的 send promise 表(public:bridge 集成测试注入/断言用)。 */
|
|
41
42
|
queueCardMessages = new Map();
|
|
42
43
|
/** Track number of executing tasks per workspace (for queue card display). */
|
|
43
44
|
pendingOrExecutingCount = new Map();
|