lark-remote 0.5.10 → 0.5.13
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 -2
- package/README.en.md +1 -14
- package/README.md +1 -14
- package/dist/binder.js +7 -1
- package/dist/bridge/approval-coordinator.d.ts +1 -0
- package/dist/bridge/approval-coordinator.js +12 -24
- package/dist/bridge/index.d.ts +7 -0
- package/dist/bridge/index.js +89 -230
- package/dist/bridge/run-approval.d.ts +15 -0
- package/dist/bridge/run-approval.js +41 -0
- package/dist/bridge/run-usage.d.ts +46 -0
- package/dist/bridge/run-usage.js +96 -0
- package/dist/bridge/session-writeback.d.ts +18 -0
- package/dist/bridge/session-writeback.js +36 -0
- package/dist/card/approval-render.d.ts +14 -0
- package/dist/card/approval-render.js +155 -29
- package/dist/card/bash-card-session.js +10 -5
- package/dist/card/bash-renderer.js +11 -23
- package/dist/card/card-budget.js +24 -4
- package/dist/card/card-session.d.ts +2 -0
- package/dist/card/card-session.js +20 -22
- package/dist/card/card-shared.d.ts +4 -4
- package/dist/card/card-shared.js +3 -9
- package/dist/card/collapsible.js +3 -2
- package/dist/card/final-answer-overflow.d.ts +7 -0
- package/dist/card/final-answer-overflow.js +46 -0
- package/dist/card/run-card-session.js +2 -7
- package/dist/card/run-renderer.d.ts +14 -0
- package/dist/card/run-renderer.js +150 -10
- package/dist/card/run-state.d.ts +12 -1
- package/dist/card/run-state.js +73 -34
- package/dist/common/text-width.js +5 -0
- package/dist/common/tool-summary.js +4 -2
- package/dist/config/codex-config.js +5 -3
- package/dist/config/index.d.ts +4 -2
- package/dist/config/index.js +19 -5
- package/dist/inbound/turn-assembler.js +6 -0
- package/dist/index.js +15 -391
- package/dist/instance-lock.d.ts +9 -1
- package/dist/instance-lock.js +42 -7
- package/dist/message-handlers.d.ts +26 -0
- package/dist/message-handlers.js +275 -0
- package/dist/platform/identity.js +4 -3
- package/dist/platform/path.js +3 -1
- package/dist/platform/terminator-win32.d.ts +13 -1
- package/dist/platform/terminator-win32.js +4 -14
- package/dist/router/card-action-payload.d.ts +1 -1
- package/dist/router/card-action-payload.js +1 -1
- package/dist/router/commands/ls.d.ts +9 -0
- package/dist/router/commands/ls.js +243 -0
- package/dist/router/commands/order.d.ts +12 -0
- package/dist/router/commands/order.js +178 -0
- package/dist/router/commands/resume.d.ts +47 -0
- package/dist/router/commands/resume.js +246 -0
- package/dist/router/commands/ws.d.ts +15 -0
- package/dist/router/commands/ws.js +189 -0
- package/dist/router/config/claude.js +3 -3
- package/dist/router/config/codex.js +2 -6
- package/dist/router/config/common/turn-idle.d.ts +2 -0
- package/dist/router/config/common/turn-idle.js +9 -0
- package/dist/router/config/kimi.js +2 -6
- package/dist/router/config/opencode.js +2 -6
- package/dist/router/index.d.ts +14 -22
- package/dist/router/index.js +93 -1133
- package/dist/router/paging.d.ts +13 -0
- package/dist/router/paging.js +20 -0
- package/dist/router/path-validation.d.ts +27 -0
- package/dist/router/path-validation.js +45 -0
- package/dist/runner/claude/runner.d.ts +3 -4
- package/dist/runner/claude/runner.js +4 -5
- package/dist/runner/claude/session.js +1 -1
- package/dist/runner/codex/app-server/runner.js +12 -0
- package/dist/runner/codex/app-server/translator.d.ts +2 -0
- package/dist/runner/codex/app-server/translator.js +65 -9
- package/dist/runner/common/acp/base-translator.d.ts +15 -8
- package/dist/runner/common/acp/base-translator.js +38 -7
- package/dist/runner/common/jsonl-stream.js +12 -1
- package/dist/runner/common/jsonrpc/connection-manager.js +11 -1
- package/dist/runner/common/jsonrpc/transport.d.ts +2 -1
- package/dist/runner/common/jsonrpc/transport.js +7 -1
- package/dist/runner/common/spawning-runner.js +10 -4
- package/dist/runner/common/turn-started.d.ts +2 -0
- package/dist/runner/common/turn-started.js +10 -0
- package/dist/runner/dsh/approval.js +16 -3
- package/dist/runner/dsh/auth.js +2 -3
- package/dist/runner/dsh/client.d.ts +32 -7
- package/dist/runner/dsh/client.js +115 -149
- package/dist/runner/dsh/daemon.d.ts +0 -2
- package/dist/runner/dsh/daemon.js +1 -6
- package/dist/runner/dsh/errors.d.ts +5 -0
- package/dist/runner/dsh/errors.js +11 -0
- package/dist/runner/dsh/exit-cancel.d.ts +0 -2
- package/dist/runner/dsh/exit-cancel.js +63 -24
- package/dist/runner/dsh/home.d.ts +2 -0
- package/dist/runner/dsh/home.js +8 -0
- package/dist/runner/dsh/inflight.d.ts +0 -2
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.d.ts +26 -0
- package/dist/runner/dsh/mux-stream.js +157 -0
- package/dist/runner/dsh/runner.d.ts +7 -0
- package/dist/runner/dsh/runner.js +42 -5
- package/dist/runner/dsh/translator.d.ts +0 -10
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.js +42 -5
- package/dist/runner/kimi/acp/translator.d.ts +8 -0
- package/dist/runner/kimi/acp/translator.js +11 -12
- package/dist/runner/opencode/acp/translator.js +1 -12
- package/dist/runner/pi/rpc/runner.js +33 -5
- package/dist/runner/pi/rpc/translator.d.ts +2 -8
- package/dist/runner/pi/rpc/translator.js +2 -7
- package/dist/runner/registry.d.ts +0 -19
- package/dist/runner/registry.js +0 -32
- package/dist/runner/resolve-agent-choices.js +5 -1
- package/dist/runner/sync-agent-choices.js +4 -1
- package/dist/runner/types.d.ts +17 -1
- package/dist/session/claude/session-index.js +13 -4
- package/dist/session/codex/rollout-reader.js +51 -26
- package/dist/session/common/usage-accumulator.d.ts +10 -1
- package/dist/session/common/usage-accumulator.js +10 -2
- package/dist/session/dsh/sessions.js +29 -9
- package/dist/session/kimi/sessions.js +11 -7
- package/dist/session/opencode/sessions.js +15 -7
- package/dist/session/pi/sessions.js +3 -2
- package/dist/session/session-store.js +4 -3
- package/package.json +1 -1
package/dist/bridge/index.js
CHANGED
|
@@ -4,18 +4,21 @@ import { isContentFilterFailure } from '../common/turn-failure.js';
|
|
|
4
4
|
import { resolveFinalUsage, errorMessage } from './final-usage.js';
|
|
5
5
|
import { getLogger } from '../logger/index.js';
|
|
6
6
|
import { RunCardSession } from '../card/run-card-session.js';
|
|
7
|
-
import { renderRunCard } from '../card/run-renderer.js';
|
|
7
|
+
import { renderRunCard, renderRunCardDetailed, } from '../card/run-renderer.js';
|
|
8
|
+
import { splitFinalAnswer } from '../card/final-answer-overflow.js';
|
|
8
9
|
import { renderBashCard } from '../card/bash-renderer.js';
|
|
9
10
|
import { BashCardSession, capBashOutput } from '../card/bash-card-session.js';
|
|
10
11
|
import { isRunTerminal } from '../card/run-state.js';
|
|
11
12
|
import { buildSessionHistoryCard } from '../router/card-helpers.js';
|
|
12
13
|
import { agentDisplayName, resumeUseButton } from '../card/card-shared.js';
|
|
13
14
|
import { enforceCardBudget } from '../card/card-budget.js';
|
|
14
|
-
import {
|
|
15
|
+
import { RunUsageCollector } from './run-usage.js';
|
|
15
16
|
import { BashProcessRunner } from '../runner/index.js';
|
|
16
17
|
import { AGENT_KINDS, supportsCompact } from '../runner/types.js';
|
|
17
18
|
import { QueueManager, } from './queue-manager.js';
|
|
18
|
-
import {
|
|
19
|
+
import { decisionToApprovalAction } from './approval-coordinator.js';
|
|
20
|
+
import { createRunApprovalCoordinator, remindPendingApproval } from './run-approval.js';
|
|
21
|
+
import { writeBackInitSession, writeBackResultSession, writeBackTurnStarted, } from './session-writeback.js';
|
|
19
22
|
import { InboundMediaHandler } from './inbound-media.js';
|
|
20
23
|
/**
|
|
21
24
|
* Max events to read for the completion notification card (mirror router's
|
|
@@ -24,8 +27,6 @@ import { InboundMediaHandler } from './inbound-media.js';
|
|
|
24
27
|
* budget.
|
|
25
28
|
*/
|
|
26
29
|
const COMPLETION_NOTIFICATION_MAX_EVENTS = 5;
|
|
27
|
-
/** Approval request timeout (ms) — pending approval expires after this. */
|
|
28
|
-
const APPROVAL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
29
30
|
/**
|
|
30
31
|
* runner 缓存的工作区数量上限:超过后创建新工作区槽位时
|
|
31
32
|
* 顺带回收「非 active 且进程已停」的死缓存,防止随访问过的工作区数量
|
|
@@ -682,8 +683,10 @@ export class Bridge {
|
|
|
682
683
|
for (const [runId, b] of bashTargets) {
|
|
683
684
|
getLogger().info(`[lark-remote] interrupt hit bash runId=${runId} cwd=${b.cwd} userId=${input.userId}`);
|
|
684
685
|
await b.bashRunner.stop({ immediate: true });
|
|
685
|
-
// Update card to show interrupted state
|
|
686
|
-
|
|
686
|
+
// Update card to show interrupted state. exitCode 不传(保持 null):
|
|
687
|
+
// bash-renderer 的「已手动终止」文案要求 exitCode === null;传 -1 会让
|
|
688
|
+
// 该分支不可达,中断卡退化成「退出码: -1」的失败样式。
|
|
689
|
+
await b.cardSession.finish('interrupted');
|
|
687
690
|
this.activeBashRuns.delete(runId);
|
|
688
691
|
getLogger().info(`[lark-remote] interrupt bash stop done runId=${runId}`);
|
|
689
692
|
stoppedCount++;
|
|
@@ -812,6 +815,29 @@ export class Bridge {
|
|
|
812
815
|
return false;
|
|
813
816
|
}
|
|
814
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Deliver a final answer that cannot fit in the run card. Plain text avoids
|
|
820
|
+
* markdown parser limits and preserves every character; sequence labels make
|
|
821
|
+
* multi-message delivery unambiguous.
|
|
822
|
+
*/
|
|
823
|
+
async sendFinalAnswerOverflow(text, ctx) {
|
|
824
|
+
const chunks = splitFinalAnswer(text);
|
|
825
|
+
try {
|
|
826
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
827
|
+
const prefix = chunks.length > 1 ? `完整回答(${i + 1}/${chunks.length})\n\n` : '完整回答\n\n';
|
|
828
|
+
await this.connector.sendWithRetry(ctx.chatId, { text: prefix + chunks[i] }, { replyTo: ctx.messageId });
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
catch (err) {
|
|
832
|
+
getLogger().error('[lark-remote] failed to deliver complete final answer:', err);
|
|
833
|
+
try {
|
|
834
|
+
await this.connector.sendWithRetry(ctx.chatId, { text: `⚠️ 完整回答补发失败:${errorMessage(err)}` }, { replyTo: ctx.messageId });
|
|
835
|
+
}
|
|
836
|
+
catch (fallbackErr) {
|
|
837
|
+
getLogger().error('[lark-remote] final-answer failure notice also failed:', fallbackErr);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
815
841
|
/**
|
|
816
842
|
* Update an existing card in place (原地更新) instead of sending a new one.
|
|
817
843
|
*
|
|
@@ -1006,6 +1032,7 @@ export class Bridge {
|
|
|
1006
1032
|
// 放在 finalize 之后:activeRuns 槽位已释放,续跑能正常开新卡片。
|
|
1007
1033
|
await this.retryAfterContentFilter({
|
|
1008
1034
|
cardSession,
|
|
1035
|
+
activeRun,
|
|
1009
1036
|
ctx,
|
|
1010
1037
|
cwd,
|
|
1011
1038
|
agentKind,
|
|
@@ -1042,6 +1069,12 @@ export class Bridge {
|
|
|
1042
1069
|
getLogger().info(`[lark-remote] content_filter auto-retry skipped: session epoch moved runId=${state.runId}`);
|
|
1043
1070
|
return;
|
|
1044
1071
|
}
|
|
1072
|
+
// 续跑必须接「本轮实际使用的 session」而非入队时刻快照(与 finalize 路径
|
|
1073
|
+
// 的三级优先同口径:卡片状态 → activeRun → 起点快照)。快照只在 run 未建立
|
|
1074
|
+
// 任何新会话时兜底。可证场景:新会话第一条消息(快照 undefined)被截断——
|
|
1075
|
+
// init 已建立 S1,epoch 未动,传快照会让续跑落到全新空会话,本轮上下文
|
|
1076
|
+
// 全部丢失;claude --resume fork 场景同理(init 上报的 id 才是权威)。
|
|
1077
|
+
const retrySessionId = state.sessionId ?? params.activeRun.sessionId ?? params.sessionId;
|
|
1045
1078
|
getLogger().warn(`[lark-remote] content_filter detected, auto-retrying once runId=${state.runId} cwd=${params.cwd}`);
|
|
1046
1079
|
await this.sendResult({
|
|
1047
1080
|
text: '🔁 上一回合被服务方内容审核截断(content_filter,通常是误报),已自动重试一次。',
|
|
@@ -1052,7 +1085,7 @@ export class Bridge {
|
|
|
1052
1085
|
cwdOverride: params.cwd,
|
|
1053
1086
|
binding: {
|
|
1054
1087
|
agent: params.agentKind,
|
|
1055
|
-
sessionId:
|
|
1088
|
+
sessionId: retrySessionId,
|
|
1056
1089
|
sessionEpoch: params.sessionEpochAtStart,
|
|
1057
1090
|
},
|
|
1058
1091
|
autoRetry: true,
|
|
@@ -1164,60 +1197,7 @@ export class Bridge {
|
|
|
1164
1197
|
// init the result was the only terminal signal we had. Keep it so the card
|
|
1165
1198
|
// shows the real error instead of the generic "no result" fallback.
|
|
1166
1199
|
let pendingPreInitTerminal;
|
|
1167
|
-
|
|
1168
|
-
let resultNotSuccess = false;
|
|
1169
|
-
// Final usage read from jsonl after the run completes. live stream-json does
|
|
1170
|
-
// not emit compact_boundary, so the live contextLength is unreliable.
|
|
1171
|
-
let finalContextLength;
|
|
1172
|
-
let finalContextLimit;
|
|
1173
|
-
let finalCompactCount;
|
|
1174
|
-
let finalCacheReadTokens;
|
|
1175
|
-
let finalCacheCreationTokens;
|
|
1176
|
-
let finalTotalTokens;
|
|
1177
|
-
// Real input/output tokens captured from the live result event (codex/
|
|
1178
|
-
// opencode carry them in ResultEvent.usage). Threaded to the card so the
|
|
1179
|
-
// done card shows real values instead of the 10% estimate.
|
|
1180
|
-
let liveInputTokens;
|
|
1181
|
-
let liveOutputTokens;
|
|
1182
|
-
// Real context window limit from the live result event (codex app-server
|
|
1183
|
-
// carries it in ResultEvent.usage.context_limit). jsonl wins when present;
|
|
1184
|
-
// live is the fallback (app-server error runs / jsonl 未落盘时).
|
|
1185
|
-
let liveContextLimit;
|
|
1186
|
-
// Final input/output for the done card: live value wins (codex/opencode
|
|
1187
|
-
// carry per-run usage in the result event); jsonl is the fallback for
|
|
1188
|
-
// agents whose live stream has no usage (kimi).
|
|
1189
|
-
let finalInputTokens;
|
|
1190
|
-
let finalOutputTokens;
|
|
1191
|
-
// Session-cumulative input/output (all runs) read from jsonl; threaded to
|
|
1192
|
-
// the done card's "累计" display. Always from jsonl (live has no cumulative).
|
|
1193
|
-
let finalCumulativeTotalTokens;
|
|
1194
|
-
let finalCumulativeInputTokens;
|
|
1195
|
-
let finalCumulativeOutputTokens;
|
|
1196
|
-
let finalCumulativeCacheReadTokens;
|
|
1197
|
-
let finalCumulativeCacheCreationTokens;
|
|
1198
|
-
// Reasoning tokens (pi usage.reasoning) from the live result event.
|
|
1199
|
-
let finalReasoningTokens;
|
|
1200
|
-
// 统一的 usage meta 构造(finalize 各分支共享,本方法内原先 5 处手写 11-15
|
|
1201
|
-
// 字段对象;第 6 处 streamCompact 形状不同未收敛)。
|
|
1202
|
-
// catch 路径需覆盖 flow 字段时在展开后覆写即可。catch error 路径亦复用此
|
|
1203
|
-
// 闭包,相比旧手写对象补齐了累计 cache 字段(cumulativeCacheReadTokens/
|
|
1204
|
-
// cumulativeCacheCreationTokens),属有意的口径对齐(向 done 路径看齐)。
|
|
1205
|
-
const usageMeta = () => ({
|
|
1206
|
-
contextLength: finalContextLength,
|
|
1207
|
-
contextLimit: finalContextLimit,
|
|
1208
|
-
compactCount: finalCompactCount,
|
|
1209
|
-
cacheReadTokens: finalCacheReadTokens,
|
|
1210
|
-
cacheCreationTokens: finalCacheCreationTokens,
|
|
1211
|
-
totalTokens: finalTotalTokens,
|
|
1212
|
-
inputTokens: finalInputTokens,
|
|
1213
|
-
outputTokens: finalOutputTokens,
|
|
1214
|
-
cumulativeTotalTokens: finalCumulativeTotalTokens,
|
|
1215
|
-
cumulativeInputTokens: finalCumulativeInputTokens,
|
|
1216
|
-
cumulativeOutputTokens: finalCumulativeOutputTokens,
|
|
1217
|
-
cumulativeCacheReadTokens: finalCumulativeCacheReadTokens,
|
|
1218
|
-
cumulativeCacheCreationTokens: finalCumulativeCacheCreationTokens,
|
|
1219
|
-
reasoningTokens: finalReasoningTokens,
|
|
1220
|
-
});
|
|
1200
|
+
const usage = new RunUsageCollector();
|
|
1221
1201
|
try {
|
|
1222
1202
|
try {
|
|
1223
1203
|
// queue.immediate 的 run-start hold:目标槽位可能在 handler 发最终
|
|
@@ -1247,7 +1227,6 @@ export class Bridge {
|
|
|
1247
1227
|
runner,
|
|
1248
1228
|
pendingApprovals: () => this.approvalCoordinators.get(runId)?.pendingCount() ?? 0,
|
|
1249
1229
|
});
|
|
1250
|
-
let contextLength;
|
|
1251
1230
|
getLogger().info(`[lark-remote] runner.run() begin runId=${runId} message=${message.slice(0, 100)}`);
|
|
1252
1231
|
// Extract agent-specific options from config
|
|
1253
1232
|
const agentOpts = this.getAgentRunOptions(agentKind);
|
|
@@ -1258,6 +1237,14 @@ export class Bridge {
|
|
|
1258
1237
|
// ending (sawResult, usage capture, or cardSession.push would all
|
|
1259
1238
|
// be wrong for the historical event).
|
|
1260
1239
|
let sawInit = false;
|
|
1240
|
+
const writeback = {
|
|
1241
|
+
sessionStore: this.sessionStore,
|
|
1242
|
+
userId: ctx.userId,
|
|
1243
|
+
agentKind,
|
|
1244
|
+
cwd,
|
|
1245
|
+
sessionEpochAtStart,
|
|
1246
|
+
runId,
|
|
1247
|
+
};
|
|
1261
1248
|
for await (const event of runner.run(message, runOpts)) {
|
|
1262
1249
|
// 只有终态才阻止事件处理。finalizing 是非终态,应该继续处理
|
|
1263
1250
|
// 这样在 result 事件后,后台任务输出仍能被正确处理
|
|
@@ -1270,34 +1257,7 @@ export class Bridge {
|
|
|
1270
1257
|
// 本轮真实使用的 session(不可变身份):指针在 run 中被 /new 等移动时,
|
|
1271
1258
|
// 结束后的 usage 读取仍以它为准。
|
|
1272
1259
|
activeRun.sessionId = event.session_id;
|
|
1273
|
-
|
|
1274
|
-
// /config 切换)移动了 session 指针,此 init 的写回是 stale 的——
|
|
1275
|
-
// 跳过,否则 /new 的清空会被在途 run 静默撤销(2026-08-09 事故:
|
|
1276
|
-
// task-notification 注入触发 Claude 重发 init,已终止的 run 被复活)。
|
|
1277
|
-
const pointerMoved = this.sessionStore.getSessionEpoch(ctx.userId, agentKind) !== sessionEpochAtStart;
|
|
1278
|
-
if (pointerMoved) {
|
|
1279
|
-
getLogger().info(`[lark-remote] system.init write-back skipped: session pointer moved since run start runId=${runId} sessionId=${event.session_id}`);
|
|
1280
|
-
}
|
|
1281
|
-
else {
|
|
1282
|
-
// Use session's real directory from event.cwd (not runner's cwd parameter)
|
|
1283
|
-
// guard empty string too -- `??` only catches null/undefined, but a
|
|
1284
|
-
// translator (or older build) may emit cwd="", which must NOT overwrite
|
|
1285
|
-
// the good runner cwd.
|
|
1286
|
-
const hasCwd = !!this.sessionStore.getCwd(ctx.userId);
|
|
1287
|
-
const realCwd = event.cwd && event.cwd.length > 0 ? event.cwd : cwd;
|
|
1288
|
-
if (hasCwd && event.cwd && event.cwd !== cwd) {
|
|
1289
|
-
// Session has its own cwd (e.g. EnterWorktree relocate). Keep the
|
|
1290
|
-
// cwd unchanged; record the session's actual cwd in sessionCwds.
|
|
1291
|
-
getLogger().info(`[lark-remote] system.init: cwd=${cwd}, session cwd=${event.cwd}`);
|
|
1292
|
-
this.sessionStore.setSessionIdAndSessionCwd(ctx.userId, agentKind, event.session_id, realCwd);
|
|
1293
|
-
}
|
|
1294
|
-
else {
|
|
1295
|
-
// First use (no cwd yet) or event.cwd matches runner cwd:
|
|
1296
|
-
// set sessionId + cwd as before; on first use also bootstrap sessionCwds.
|
|
1297
|
-
const bootstrapSessionCwd = !hasCwd && event.cwd && event.cwd.length > 0 ? event.cwd : undefined;
|
|
1298
|
-
this.sessionStore.setSessionIdAndCwd(ctx.userId, agentKind, event.session_id, realCwd, bootstrapSessionCwd);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1260
|
+
writeBackInitSession(writeback, event);
|
|
1301
1261
|
getLogger().info(`[lark-remote] system.init received runId=${runId} sessionId=${event.session_id} cwd=${event.cwd}`);
|
|
1302
1262
|
}
|
|
1303
1263
|
if (event.type === 'turn_started') {
|
|
@@ -1307,20 +1267,13 @@ export class Bridge {
|
|
|
1307
1267
|
// 都新建线程(thread/start 而非 thread/resume),会话无法延续。
|
|
1308
1268
|
// init 缺失时以 threadId 作为本轮身份兜底。
|
|
1309
1269
|
activeRun.sessionId ??= event.threadId;
|
|
1310
|
-
|
|
1311
|
-
if (!pointerMoved) {
|
|
1312
|
-
this.sessionStore.setSessionIdAndCwd(ctx.userId, agentKind, event.threadId, cwd);
|
|
1313
|
-
getLogger().info(`[lark-remote] turn_started session write-back runId=${runId} sessionId=${event.threadId}`);
|
|
1314
|
-
}
|
|
1315
|
-
else {
|
|
1316
|
-
getLogger().info(`[lark-remote] turn_started write-back skipped: session pointer moved since run start runId=${runId} sessionId=${event.threadId}`);
|
|
1317
|
-
}
|
|
1270
|
+
writeBackTurnStarted(writeback, event);
|
|
1318
1271
|
}
|
|
1319
1272
|
// Track context length from compaction events
|
|
1320
1273
|
if (event.type === 'system' &&
|
|
1321
1274
|
event.subtype === 'compact_boundary' &&
|
|
1322
1275
|
event.compactMetadata) {
|
|
1323
|
-
|
|
1276
|
+
usage.observeCompaction(event.compactMetadata.postTokens);
|
|
1324
1277
|
}
|
|
1325
1278
|
if (event.type === 'result') {
|
|
1326
1279
|
// Pre-init result guard: Claude CLI --resume emits a stale result
|
|
@@ -1360,7 +1313,7 @@ export class Bridge {
|
|
|
1360
1313
|
if (!preInitResult) {
|
|
1361
1314
|
sawResult = true;
|
|
1362
1315
|
}
|
|
1363
|
-
|
|
1316
|
+
usage.observeResult(event);
|
|
1364
1317
|
getLogger().info(preInitResult
|
|
1365
1318
|
? `[lark-remote] pre-init result from live runner (app-server setup failure) runId=${runId}`
|
|
1366
1319
|
: `[lark-remote] result event received runId=${runId}`);
|
|
@@ -1369,83 +1322,22 @@ export class Bridge {
|
|
|
1369
1322
|
// 里兜底上报。此处仅当 runner 声明 live usage(app-server)
|
|
1370
1323
|
// 且会话指针未动时写回,避免下条消息再开一个孤儿线程。其余 agent 不在
|
|
1371
1324
|
// 此列(它们各自有 system.init / turn_started 写回)。
|
|
1372
|
-
if (usageAuthority === 'live'
|
|
1373
|
-
event.session_id
|
|
1374
|
-
this.sessionStore.getSessionEpoch(ctx.userId, agentKind) === sessionEpochAtStart) {
|
|
1375
|
-
this.sessionStore.setSessionIdAndCwd(ctx.userId, agentKind, event.session_id, cwd);
|
|
1376
|
-
getLogger().info(`[lark-remote] result session write-back runId=${runId} sessionId=${event.session_id}`);
|
|
1377
|
-
}
|
|
1378
|
-
// result 后进入 finalizing 非终态(暂存 subtype/errorMsg),由 for-await
|
|
1379
|
-
// 自然结束后在 finally 转 done/error。提取 usage 信息。
|
|
1380
|
-
if (event.usage) {
|
|
1381
|
-
const u = normalizeResultUsage(event.usage);
|
|
1382
|
-
// input_tokens is non-cached (ccusage-aligned), so the context
|
|
1383
|
-
// length fallback must include cached tokens. Prefer the agent's
|
|
1384
|
-
// declared total; otherwise reconstruct from the parts.
|
|
1385
|
-
if (!contextLength) {
|
|
1386
|
-
contextLength = u.contextLength;
|
|
1387
|
-
}
|
|
1388
|
-
// Capture real input/output for the done card (avoiding the 10%
|
|
1389
|
-
// estimate in formatUsageStats). live 值优先:有 live input/output 时
|
|
1390
|
-
// 所有 flow 字段统一用 live scope(本 run),避免同一张卡片上
|
|
1391
|
-
// Input/Output 是本 run、Cache/Total 是 session 累计的混 scope 问题。
|
|
1392
|
-
liveInputTokens = u.inputTokens;
|
|
1393
|
-
liveOutputTokens = u.outputTokens;
|
|
1394
|
-
liveContextLimit = u.contextLimit;
|
|
1395
|
-
if (u.cacheReadTokens !== undefined) {
|
|
1396
|
-
finalCacheReadTokens = u.cacheReadTokens;
|
|
1397
|
-
}
|
|
1398
|
-
if (u.cacheCreationTokens !== undefined) {
|
|
1399
|
-
finalCacheCreationTokens = u.cacheCreationTokens;
|
|
1400
|
-
}
|
|
1401
|
-
if (u.totalTokens !== undefined) {
|
|
1402
|
-
finalTotalTokens = u.totalTokens;
|
|
1403
|
-
}
|
|
1404
|
-
if (u.reasoningTokens !== undefined) {
|
|
1405
|
-
finalReasoningTokens = u.reasoningTokens;
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1325
|
+
if (usageAuthority === 'live')
|
|
1326
|
+
writeBackResultSession(writeback, event.session_id);
|
|
1408
1327
|
}
|
|
1409
1328
|
// Approval events (Codex app-server mode)
|
|
1410
1329
|
if (event.type === 'approval_requested') {
|
|
1411
1330
|
// 审批请求到达:撤回 Typing 表情再重发,让飞书重新触发提醒,把用户
|
|
1412
1331
|
// 注意力拉回待审批的卡片(run 在等待人工决策,Typing 应重新闪烁)。
|
|
1413
|
-
|
|
1414
|
-
try {
|
|
1415
|
-
await this.connector.removeReactionByEmoji(ctx.messageId, 'Typing');
|
|
1416
|
-
}
|
|
1417
|
-
catch (err) {
|
|
1418
|
-
getLogger().warn(`[lark-remote] remove Typing reaction failed runId=${runId}: ${errorMessage(err)}`);
|
|
1419
|
-
}
|
|
1420
|
-
try {
|
|
1421
|
-
await this.connector.addReaction(ctx.messageId, 'Typing');
|
|
1422
|
-
}
|
|
1423
|
-
catch (err) {
|
|
1424
|
-
getLogger().warn(`[lark-remote] re-add Typing reaction failed runId=${runId}: ${errorMessage(err)}`);
|
|
1425
|
-
}
|
|
1426
|
-
})();
|
|
1332
|
+
remindPendingApproval(this.connector, ctx.messageId, runId);
|
|
1427
1333
|
let coordinator = this.approvalCoordinators.get(runId);
|
|
1428
1334
|
if (!coordinator) {
|
|
1429
|
-
coordinator =
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
responder: async (requestId, response) => {
|
|
1436
|
-
const r = runner;
|
|
1437
|
-
if (typeof r.respondApproval === 'function') {
|
|
1438
|
-
await r.respondApproval(requestId, response);
|
|
1439
|
-
}
|
|
1440
|
-
},
|
|
1441
|
-
interruptTurn: async () => {
|
|
1442
|
-
// DSH may return a StopOutcome; the coordinator only awaits.
|
|
1443
|
-
await runner.stop();
|
|
1444
|
-
},
|
|
1445
|
-
pushToCard: async (events) => {
|
|
1446
|
-
for (const ev of events)
|
|
1447
|
-
await cardSession.push(ev);
|
|
1448
|
-
},
|
|
1335
|
+
coordinator = createRunApprovalCoordinator({
|
|
1336
|
+
runner,
|
|
1337
|
+
cardSession,
|
|
1338
|
+
config: this.config,
|
|
1339
|
+
runId,
|
|
1340
|
+
agentKind,
|
|
1449
1341
|
});
|
|
1450
1342
|
this.approvalCoordinators.set(runId, coordinator);
|
|
1451
1343
|
getLogger().info(`[lark-remote] approval coordinator created runId=${runId.slice(0, 8)} requestId=${event.requestId}`);
|
|
@@ -1484,44 +1376,11 @@ export class Bridge {
|
|
|
1484
1376
|
// 优先级:卡片状态(本轮 init 写入)→ activeRun(本轮 init/turn_started)
|
|
1485
1377
|
// → 本轮开始时捕获的 sessionId。三者都没有就跳过 usage,绝不回读全局指针。
|
|
1486
1378
|
const finalSessionId = cardSession.currentState.sessionId ?? activeRun.sessionId ?? sessionId;
|
|
1487
|
-
const finalUsage = resultNotSuccess
|
|
1379
|
+
const finalUsage = usage.resultNotSuccess
|
|
1488
1380
|
? undefined
|
|
1489
1381
|
: resolveFinalUsage(this.sessionReaderRegistry, this.config.defaultAgent, finalSessionId, cwd, agentKind);
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
finalCompactCount = finalUsage?.compactCount;
|
|
1493
|
-
finalCumulativeTotalTokens = finalUsage?.cumulativeTotalTokens;
|
|
1494
|
-
finalCumulativeInputTokens = finalUsage?.cumulativeInputTokens;
|
|
1495
|
-
finalCumulativeOutputTokens = finalUsage?.cumulativeOutputTokens;
|
|
1496
|
-
finalCumulativeCacheReadTokens = finalUsage?.cumulativeCacheReadTokens;
|
|
1497
|
-
finalCumulativeCacheCreationTokens = finalUsage?.cumulativeCacheCreationTokens;
|
|
1498
|
-
// 统一 flow 字段 scope:live 优先,jsonl 兜底。
|
|
1499
|
-
// 当 live result 事件带了 input/output(即 liveInputTokens/liveOutputTokens
|
|
1500
|
-
// 非 undefined),所有 flow 字段统一用 live scope(本 run),不用 jsonl
|
|
1501
|
-
// 的 session 累计值覆盖,避免 cache%/Total 分子分母不同源。
|
|
1502
|
-
// 无 live usage 时(如 kimi),jsonl 值兜底。
|
|
1503
|
-
// contextLength/compactCount 保持 jsonl 优先(它们是水位/历史计数)。
|
|
1504
|
-
const codexJsonlFirst = usageAuthority === 'jsonl';
|
|
1505
|
-
if (!codexJsonlFirst && liveInputTokens !== undefined && liveOutputTokens !== undefined) {
|
|
1506
|
-
// 非 codex 且 live 有 input/output → 所有 flow 字段用 live scope
|
|
1507
|
-
finalInputTokens = liveInputTokens;
|
|
1508
|
-
finalOutputTokens = liveOutputTokens;
|
|
1509
|
-
// cacheReadTokens/cacheCreationTokens/totalTokens
|
|
1510
|
-
// 已在上面 result 事件处理中赋了 live 值;若 live 没有则为 undefined,
|
|
1511
|
-
// 由 formatUsageStats 的 max/兜底公式处理。
|
|
1512
|
-
}
|
|
1513
|
-
else {
|
|
1514
|
-
// codex(jsonl 优先)或无 live usage → jsonl 兜底所有 flow 字段。
|
|
1515
|
-
// jsonl 的非累计字段已是末轮(本 run)scope(claude reader 修复后对齐
|
|
1516
|
-
// codex `last_token_usage` 语义,见 scalarScan),不再是 session 累计;
|
|
1517
|
-
// session 累计只经 cumulative* 字段透传,卡片"本 run ≤ 累计"不变量成立。
|
|
1518
|
-
finalInputTokens = finalUsage?.inputTokens ?? liveInputTokens;
|
|
1519
|
-
finalOutputTokens = finalUsage?.outputTokens ?? liveOutputTokens;
|
|
1520
|
-
finalCacheReadTokens = finalUsage?.cacheReadTokens ?? finalCacheReadTokens;
|
|
1521
|
-
finalCacheCreationTokens = finalUsage?.cacheCreationTokens ?? finalCacheCreationTokens;
|
|
1522
|
-
finalTotalTokens = finalUsage?.totalTokens ?? finalTotalTokens;
|
|
1523
|
-
}
|
|
1524
|
-
getLogger().info(`[lark-remote] final usage runId=${runId} contextLength=${finalContextLength} contextLimit=${finalContextLimit ?? 'undefined'} compactCount=${finalCompactCount ?? 'undefined'} cacheRead=${finalCacheReadTokens ?? 'undefined'} cacheCreate=${finalCacheCreationTokens ?? 'undefined'}`);
|
|
1382
|
+
usage.finalize(finalUsage, usageAuthority);
|
|
1383
|
+
getLogger().info(usage.logSummary(runId));
|
|
1525
1384
|
// for-await 自然结束 = stream 关闭 = CLI 进程退出。
|
|
1526
1385
|
// 进程退出(for-await end)= 唯一终态触发源。
|
|
1527
1386
|
// 1) finalizing(result 已收到)-> 转 done/error + usage meta
|
|
@@ -1546,7 +1405,7 @@ export class Bridge {
|
|
|
1546
1405
|
await cardSession.finish(terminal, {
|
|
1547
1406
|
resultSubtype: subtype,
|
|
1548
1407
|
errorMsg: finalState.errorMsg,
|
|
1549
|
-
...
|
|
1408
|
+
...usage.meta(),
|
|
1550
1409
|
});
|
|
1551
1410
|
}
|
|
1552
1411
|
else if (finalState.terminal === 'running') {
|
|
@@ -1554,14 +1413,14 @@ export class Bridge {
|
|
|
1554
1413
|
const terminal = pendingPreInitTerminal.subtype === 'interrupted' ? 'interrupted' : 'error';
|
|
1555
1414
|
getLogger().warn(`[lark-remote] stream ended without init; using pre-init ${terminal} result runId=${runId} error=${pendingPreInitTerminal.errorMessage ?? '(none)'}`);
|
|
1556
1415
|
await cardSession.finish(terminal, {
|
|
1557
|
-
...
|
|
1416
|
+
...usage.meta(),
|
|
1558
1417
|
errorMsg: pendingPreInitTerminal.errorMessage ??
|
|
1559
1418
|
agentDisplayName(agentKind) + (terminal === 'interrupted' ? ' 已中断' : ' 启动失败'),
|
|
1560
1419
|
});
|
|
1561
1420
|
}
|
|
1562
1421
|
else {
|
|
1563
1422
|
await cardSession.finish('error', {
|
|
1564
|
-
...
|
|
1423
|
+
...usage.meta(),
|
|
1565
1424
|
errorMsg: agentDisplayName(agentKind) + ' 输出流已结束,但未收到 result 事件',
|
|
1566
1425
|
});
|
|
1567
1426
|
}
|
|
@@ -1569,7 +1428,7 @@ export class Bridge {
|
|
|
1569
1428
|
else if (sawResult) {
|
|
1570
1429
|
// 已终态(interrupted/idle_timeout)+ result 已收到:首终态优先,
|
|
1571
1430
|
// 仅补充 usage meta(finalizing 期间被 /stop 或 idle 超时也展示 token 统计)
|
|
1572
|
-
await cardSession.finish(finalState.terminal,
|
|
1431
|
+
await cardSession.finish(finalState.terminal, usage.meta());
|
|
1573
1432
|
}
|
|
1574
1433
|
// else: 已终态且未收到 result(如 spawn 失败后 interrupted)-> 无 usage 可补,跳过
|
|
1575
1434
|
}
|
|
@@ -1588,16 +1447,16 @@ export class Bridge {
|
|
|
1588
1447
|
const catchTerminal = cardSession.currentState.terminal;
|
|
1589
1448
|
if (catchTerminal === 'running' || catchTerminal === 'finalizing') {
|
|
1590
1449
|
await cardSession.finish('error', {
|
|
1591
|
-
...
|
|
1592
|
-
inputTokens: liveInputTokens,
|
|
1593
|
-
outputTokens: liveOutputTokens,
|
|
1450
|
+
...usage.meta(),
|
|
1451
|
+
inputTokens: usage.liveInputTokens,
|
|
1452
|
+
outputTokens: usage.liveOutputTokens,
|
|
1594
1453
|
errorMsg: errorMessage(err),
|
|
1595
1454
|
});
|
|
1596
1455
|
}
|
|
1597
1456
|
else {
|
|
1598
1457
|
getLogger().info(`[lark-remote] skip error finish: state already terminal (${catchTerminal}) runId=${runId}`);
|
|
1599
1458
|
if (sawResult) {
|
|
1600
|
-
await cardSession.finish(catchTerminal,
|
|
1459
|
+
await cardSession.finish(catchTerminal, usage.meta());
|
|
1601
1460
|
}
|
|
1602
1461
|
}
|
|
1603
1462
|
}
|
|
@@ -1650,10 +1509,11 @@ export class Bridge {
|
|
|
1650
1509
|
sessionFallbackAvailable: !!(cardSession.currentState.sessionId ??
|
|
1651
1510
|
this.sessionStore.getSessionId(ctx.userId, activeRun.agentKind)),
|
|
1652
1511
|
};
|
|
1512
|
+
const finalRender = renderRunCardDetailed(cardSession.currentState, finalRenderOptions);
|
|
1653
1513
|
this.rememberRunCardSnapshot(runId, cardSession.currentState, finalRenderOptions, cwd, activeRun.agentKind);
|
|
1654
1514
|
let runCardSent = false;
|
|
1655
1515
|
if (settleResult === 'unsent') {
|
|
1656
|
-
const sent = await this.sendResult({ card:
|
|
1516
|
+
const sent = await this.sendResult({ card: finalRender.card }, ctx);
|
|
1657
1517
|
runCardSent = sent;
|
|
1658
1518
|
}
|
|
1659
1519
|
else {
|
|
@@ -1666,6 +1526,9 @@ export class Bridge {
|
|
|
1666
1526
|
if (sessionId && runCardSent) {
|
|
1667
1527
|
await this.sendCompletionNotificationCard(sessionId, cwd, ctx, activeRun.agentKind);
|
|
1668
1528
|
}
|
|
1529
|
+
if (finalRender.finalAnswerStatus === 'overflow' && finalRender.finalAnswerText) {
|
|
1530
|
+
await this.sendFinalAnswerOverflow(finalRender.finalAnswerText, ctx);
|
|
1531
|
+
}
|
|
1669
1532
|
// Add a terminal-state emoji reaction to the user's original message
|
|
1670
1533
|
this.finishTurnReactions(ctx, terminalReactionEmoji(cardSession.currentState.terminal));
|
|
1671
1534
|
}
|
|
@@ -1806,27 +1669,21 @@ export class Bridge {
|
|
|
1806
1669
|
* 路由到 ApprovalCoordinator.toggleAnswer。
|
|
1807
1670
|
*/
|
|
1808
1671
|
async handleApprovalAnswer(opts) {
|
|
1809
|
-
|
|
1810
|
-
log.info(`[lark-remote] handleApprovalAnswer runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1811
|
-
return this.withCoordinator(opts.runId, (coordinator) => coordinator.toggleAnswer({ questionIndex: opts.questionIndex, option: opts.option }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1672
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswer', (coordinator) => coordinator.toggleAnswer({ questionIndex: opts.questionIndex, option: opts.option }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1812
1673
|
}
|
|
1813
1674
|
/**
|
|
1814
1675
|
* Claude AskUserQuestion 多选问题的「提交答案」按钮。
|
|
1815
1676
|
* 路由到 ApprovalCoordinator.submitAnswers。
|
|
1816
1677
|
*/
|
|
1817
1678
|
async handleApprovalAnswerSubmit(opts) {
|
|
1818
|
-
|
|
1819
|
-
log.info(`[lark-remote] handleApprovalAnswerSubmit runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1820
|
-
return this.withCoordinator(opts.runId, (coordinator) => coordinator.submitAnswers({ questionIndex: opts.questionIndex }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1679
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswerSubmit', (coordinator) => coordinator.submitAnswers({ questionIndex: opts.questionIndex }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1821
1680
|
}
|
|
1822
1681
|
/**
|
|
1823
1682
|
* Claude AskUserQuestion 自定义答案(Other):自由文本直接
|
|
1824
1683
|
* 作为该单选问题的答案提交。路由到 ApprovalCoordinator.answerCustom。
|
|
1825
1684
|
*/
|
|
1826
1685
|
async handleApprovalAnswerCustom(opts) {
|
|
1827
|
-
|
|
1828
|
-
log.info(`[lark-remote] handleApprovalAnswerCustom runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1829
|
-
return this.withCoordinator(opts.runId, (coordinator) => coordinator.answerCustom({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1686
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswerCustom', (coordinator) => coordinator.answerCustom({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1830
1687
|
}
|
|
1831
1688
|
/**
|
|
1832
1689
|
* Codex AskUserQuestion 补充说明(user_note,2026-08-18 live 验证模型理解
|
|
@@ -1834,9 +1691,11 @@ export class Bridge {
|
|
|
1834
1691
|
* 路由到 ApprovalCoordinator.answerNote。
|
|
1835
1692
|
*/
|
|
1836
1693
|
async handleApprovalAnswerNote(opts) {
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1694
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswerNote', (coordinator) => coordinator.answerNote({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
1695
|
+
}
|
|
1696
|
+
dispatchQuestionAction(opts, action, invoke) {
|
|
1697
|
+
getLogger().info(`[lark-remote] ${action} runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
1698
|
+
return this.withCoordinator(opts.runId, invoke);
|
|
1840
1699
|
}
|
|
1841
1700
|
/**
|
|
1842
1701
|
* 记下一次「可压缩 run」:终态卡上的 Compact 按钮点击时据此反查
|
|
@@ -2317,8 +2176,8 @@ export class Bridge {
|
|
|
2317
2176
|
try {
|
|
2318
2177
|
await this.connector.sendWithRetry(ctx.chatId, { card: renderBashCard(cardSession.currentState, {}) }, { replyTo: ctx.messageId });
|
|
2319
2178
|
}
|
|
2320
|
-
catch {
|
|
2321
|
-
|
|
2179
|
+
catch (fallbackErr) {
|
|
2180
|
+
getLogger().error(`[lark-remote] bash terminal fallback card failed runId=${runId} chatId=${ctx.chatId}:`, fallbackErr);
|
|
2322
2181
|
}
|
|
2323
2182
|
}
|
|
2324
2183
|
if (this.activeBashRuns.has(runId)) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AppConfig } from '../config/index.js';
|
|
2
|
+
import type { AgentKind, Runner } from '../runner/index.js';
|
|
3
|
+
import type { RunCardSession } from '../card/run-card-session.js';
|
|
4
|
+
import { ApprovalCoordinator } from './approval-coordinator.js';
|
|
5
|
+
export declare function remindPendingApproval(connector: {
|
|
6
|
+
removeReactionByEmoji(messageId: string, emoji: string): Promise<void>;
|
|
7
|
+
addReaction(messageId: string, emoji: string): Promise<void>;
|
|
8
|
+
}, messageId: string, runId: string): void;
|
|
9
|
+
export declare function createRunApprovalCoordinator(opts: {
|
|
10
|
+
runner: Runner;
|
|
11
|
+
cardSession: RunCardSession;
|
|
12
|
+
config: AppConfig;
|
|
13
|
+
runId: string;
|
|
14
|
+
agentKind: AgentKind;
|
|
15
|
+
}): ApprovalCoordinator;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DEFAULT_APPROVAL_TIMEOUT_MS } from '../config/index.js';
|
|
2
|
+
import { ApprovalCoordinator } from './approval-coordinator.js';
|
|
3
|
+
import { getLogger } from '../logger/index.js';
|
|
4
|
+
import { errorMessage } from './final-usage.js';
|
|
5
|
+
export function remindPendingApproval(connector, messageId, runId) {
|
|
6
|
+
void (async () => {
|
|
7
|
+
try {
|
|
8
|
+
await connector.removeReactionByEmoji(messageId, 'Typing');
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
getLogger().warn(`[lark-remote] remove Typing reaction failed runId=${runId}: ${errorMessage(err)}`);
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
await connector.addReaction(messageId, 'Typing');
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
getLogger().warn(`[lark-remote] re-add Typing reaction failed runId=${runId}: ${errorMessage(err)}`);
|
|
18
|
+
}
|
|
19
|
+
})();
|
|
20
|
+
}
|
|
21
|
+
export function createRunApprovalCoordinator(opts) {
|
|
22
|
+
return new ApprovalCoordinator({
|
|
23
|
+
approvalTimeoutMs: opts.agentKind === 'claude'
|
|
24
|
+
? (opts.config.claude?.approvalTimeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS)
|
|
25
|
+
: DEFAULT_APPROVAL_TIMEOUT_MS,
|
|
26
|
+
responder: async (requestId, response) => {
|
|
27
|
+
const runner = opts.runner;
|
|
28
|
+
if (typeof runner.respondApproval === 'function') {
|
|
29
|
+
await runner.respondApproval(requestId, response);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
interruptTurn: async () => {
|
|
33
|
+
await opts.runner.stop();
|
|
34
|
+
},
|
|
35
|
+
pushToCard: async (events) => {
|
|
36
|
+
for (const event of events)
|
|
37
|
+
await opts.cardSession.push(event);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=run-approval.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ResultEvent } from '../runner/types.js';
|
|
2
|
+
import type { FinalUsageSnapshot } from './final-usage.js';
|
|
3
|
+
export type UsageAuthority = 'live' | 'jsonl';
|
|
4
|
+
export declare class RunUsageCollector {
|
|
5
|
+
private contextLength?;
|
|
6
|
+
private finalContextLength?;
|
|
7
|
+
private finalContextLimit?;
|
|
8
|
+
private finalCompactCount?;
|
|
9
|
+
private finalCacheReadTokens?;
|
|
10
|
+
private finalCacheCreationTokens?;
|
|
11
|
+
private finalTotalTokens?;
|
|
12
|
+
private liveInput?;
|
|
13
|
+
private liveOutput?;
|
|
14
|
+
private liveContextLimit?;
|
|
15
|
+
private finalInputTokens?;
|
|
16
|
+
private finalOutputTokens?;
|
|
17
|
+
private finalCumulativeTotalTokens?;
|
|
18
|
+
private finalCumulativeInputTokens?;
|
|
19
|
+
private finalCumulativeOutputTokens?;
|
|
20
|
+
private finalCumulativeCacheReadTokens?;
|
|
21
|
+
private finalCumulativeCacheCreationTokens?;
|
|
22
|
+
private finalReasoningTokens?;
|
|
23
|
+
resultNotSuccess: boolean;
|
|
24
|
+
get liveInputTokens(): number | undefined;
|
|
25
|
+
get liveOutputTokens(): number | undefined;
|
|
26
|
+
observeCompaction(postTokens: number): void;
|
|
27
|
+
observeResult(event: ResultEvent): void;
|
|
28
|
+
finalize(finalUsage: FinalUsageSnapshot | undefined, authority: UsageAuthority | undefined): void;
|
|
29
|
+
meta(): {
|
|
30
|
+
contextLength?: number;
|
|
31
|
+
contextLimit?: number;
|
|
32
|
+
compactCount?: number;
|
|
33
|
+
cacheReadTokens?: number;
|
|
34
|
+
cacheCreationTokens?: number;
|
|
35
|
+
totalTokens?: number;
|
|
36
|
+
inputTokens?: number;
|
|
37
|
+
outputTokens?: number;
|
|
38
|
+
cumulativeTotalTokens?: number;
|
|
39
|
+
cumulativeInputTokens?: number;
|
|
40
|
+
cumulativeOutputTokens?: number;
|
|
41
|
+
cumulativeCacheReadTokens?: number;
|
|
42
|
+
cumulativeCacheCreationTokens?: number;
|
|
43
|
+
reasoningTokens?: number;
|
|
44
|
+
};
|
|
45
|
+
logSummary(runId: string): string;
|
|
46
|
+
}
|