lark-remote 0.5.18 → 0.5.20
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 +32 -0
- package/dist/bridge/approval-actions.d.ts +1 -1
- package/dist/bridge/approval-actions.js +1 -1
- package/dist/bridge/bash-run.d.ts +71 -0
- package/dist/bridge/bash-run.js +217 -0
- package/dist/bridge/compact.d.ts +155 -0
- package/dist/bridge/compact.js +450 -0
- package/dist/bridge/final-usage.js +4 -4
- package/dist/bridge/inbound-media.js +1 -1
- package/dist/bridge/index.d.ts +120 -119
- package/dist/bridge/index.js +257 -695
- package/dist/bridge/job-watch.d.ts +68 -0
- package/dist/bridge/job-watch.js +146 -0
- package/dist/bridge/queue-manager.d.ts +3 -3
- package/dist/bridge/queue-manager.js +10 -29
- package/dist/card/approval-render.d.ts +2 -2
- package/dist/card/approval-render.js +109 -97
- package/dist/card/bash-card-session.js +2 -0
- package/dist/card/card-budget.js +1 -1
- package/dist/card/card-helpers.d.ts +3 -3
- package/dist/card/card-helpers.js +6 -6
- package/dist/card/card-session.d.ts +17 -0
- package/dist/card/card-session.js +35 -0
- package/dist/card/final-answer-file.js +1 -1
- package/dist/card/run-card-session.js +2 -0
- package/dist/card/run-renderer.d.ts +3 -3
- package/dist/card/run-renderer.js +77 -102
- package/dist/card/run-state.d.ts +35 -8
- package/dist/card/run-state.js +117 -90
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +1 -1
- package/dist/card/usage-format.d.ts +12 -0
- package/dist/card/usage-format.js +8 -3
- package/dist/cli.js +15 -3
- package/dist/clone.js +2 -2
- package/dist/config/codex-config.js +130 -106
- package/dist/config/index.d.ts +16 -2
- package/dist/config/index.js +23 -8
- package/dist/connector/index.js +6 -6
- package/dist/error-classification.js +3 -2
- package/dist/inbound/placeholder.d.ts +1 -1
- package/dist/inbound/placeholder.js +1 -1
- package/dist/index.js +203 -133
- package/dist/logger/index.js +6 -6
- package/dist/message-handlers.d.ts +0 -5
- package/dist/message-handlers.js +4 -2
- package/dist/model-catalog.d.ts +2 -2
- package/dist/model-catalog.js +3 -8
- package/dist/platform/identity.js +1 -1
- package/dist/platform/path.d.ts +1 -1
- package/dist/platform/path.js +1 -1
- package/dist/platform/sleep-blocker.js +1 -1
- package/dist/platform/terminator-win32.js +2 -1
- package/dist/router/commands/resume.js +2 -20
- package/dist/router/config/dsh.d.ts +4 -8
- package/dist/router/config/dsh.js +3 -10
- package/dist/router/config/kimi.js +1 -1
- package/dist/router/config/types.d.ts +1 -1
- package/dist/router/config-diff.js +1 -1
- package/dist/router/index.d.ts +56 -11
- package/dist/router/index.js +222 -202
- package/dist/runner/claude/session.d.ts +1 -1
- package/dist/runner/claude/session.js +6 -4
- package/dist/runner/codex/app-server/runner.d.ts +9 -2
- package/dist/runner/codex/app-server/runner.js +30 -25
- package/dist/runner/codex/app-server/translator.d.ts +0 -5
- package/dist/runner/codex/app-server/translator.js +4 -17
- package/dist/runner/common/acp/acp-approval.d.ts +1 -1
- package/dist/runner/common/acp/acp-approval.js +1 -1
- package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
- package/dist/runner/common/acp/base-acp-runner.js +26 -65
- package/dist/runner/common/acp/base-translator.d.ts +12 -2
- package/dist/runner/common/acp/base-translator.js +31 -9
- package/dist/runner/common/acp/protocol-types.d.ts +1 -1
- package/dist/runner/common/acp/protocol-types.js +2 -2
- package/dist/runner/common/connection-based-runner.d.ts +32 -1
- package/dist/runner/common/connection-based-runner.js +32 -2
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
- package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
- package/dist/runner/common/spawning-runner.d.ts +1 -1
- package/dist/runner/common/spawning-runner.js +1 -1
- package/dist/runner/dsh/catalog-probe.d.ts +17 -0
- package/dist/runner/dsh/catalog-probe.js +10 -0
- package/dist/runner/dsh/client.d.ts +13 -8
- package/dist/runner/dsh/client.js +23 -15
- package/dist/runner/dsh/exit-cancel.d.ts +2 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +1 -1
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/runner.d.ts +48 -2
- package/dist/runner/dsh/runner.js +288 -199
- package/dist/runner/dsh/translator.d.ts +1 -1
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +1 -2
- package/dist/runner/kimi/acp/runner.js +1 -2
- package/dist/runner/kimi/acp/translator.d.ts +1 -1
- package/dist/runner/kimi/acp/translator.js +3 -4
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
- package/dist/runner/pi/rpc/protocol-types.js +1 -1
- package/dist/runner/pi/rpc/runner.d.ts +3 -0
- package/dist/runner/pi/rpc/runner.js +27 -42
- package/dist/runner/types.d.ts +25 -8
- package/dist/runner/types.js +1 -1
- package/dist/session/claude/sessions.d.ts +45 -0
- package/dist/session/claude/sessions.js +97 -71
- package/dist/session/codex/rollout-reader.js +4 -4
- package/dist/session/common/throw-session-list-error.d.ts +10 -0
- package/dist/session/common/throw-session-list-error.js +16 -0
- package/dist/session/dsh/sessions.js +11 -5
- package/dist/session/kimi/sessions.d.ts +40 -1
- package/dist/session/kimi/sessions.js +244 -192
- package/dist/session/opencode/sessions.d.ts +153 -1
- package/dist/session/opencode/sessions.js +354 -321
- package/dist/session/pi/sessions.js +4 -5
- package/dist/session/session-store.d.ts +1 -1
- package/dist/session/session-store.js +1 -1
- package/dist/update/version-check.js +2 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.js +1 -1
- package/package.json +1 -1
package/dist/card/run-state.js
CHANGED
|
@@ -125,8 +125,6 @@ function reduceRunStateInner(state, event) {
|
|
|
125
125
|
}
|
|
126
126
|
/** Handle app-server turn_started — records the operation kind (turn vs compact). */
|
|
127
127
|
function reduceTurnStartedEvent(state, event) {
|
|
128
|
-
if (event.type !== 'turn_started')
|
|
129
|
-
return state;
|
|
130
128
|
return {
|
|
131
129
|
...state,
|
|
132
130
|
operationKind: event.operationKind,
|
|
@@ -142,8 +140,6 @@ function reduceTurnStartedEvent(state, event) {
|
|
|
142
140
|
* the active streaming content sits at the bottom, like the exec path.
|
|
143
141
|
*/
|
|
144
142
|
function reduceTurnDiffEvent(state, event) {
|
|
145
|
-
if (event.type !== 'turn_diff')
|
|
146
|
-
return state;
|
|
147
143
|
let next = state;
|
|
148
144
|
const ts = event.timestamp;
|
|
149
145
|
const complete = event.complete === true;
|
|
@@ -170,12 +166,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
170
166
|
...(complete ? { completedAt: ts } : {}),
|
|
171
167
|
};
|
|
172
168
|
}, !complete);
|
|
173
|
-
next =
|
|
174
|
-
...next,
|
|
175
|
-
blocks: keepLatestBlocks(capped),
|
|
176
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
177
|
-
footer: 'streaming',
|
|
178
|
-
};
|
|
169
|
+
next = withCappedBlocks(next, capped, 'streaming');
|
|
179
170
|
}
|
|
180
171
|
if (event.reasoning !== undefined) {
|
|
181
172
|
const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'thinking' && b.itemId === event.itemId, () => ({
|
|
@@ -195,12 +186,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
195
186
|
...(complete ? { completedAt: ts } : {}),
|
|
196
187
|
};
|
|
197
188
|
}, !complete);
|
|
198
|
-
next =
|
|
199
|
-
...next,
|
|
200
|
-
blocks: keepLatestBlocks(capped),
|
|
201
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
202
|
-
footer: 'thinking',
|
|
203
|
-
};
|
|
189
|
+
next = withCappedBlocks(next, capped, 'thinking');
|
|
204
190
|
}
|
|
205
191
|
if (event.toolOutput !== undefined) {
|
|
206
192
|
const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'tool' && b.tool.id === event.itemId, () => {
|
|
@@ -231,12 +217,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
231
217
|
...(complete ? { completedAt: ts } : {}),
|
|
232
218
|
},
|
|
233
219
|
}), !complete);
|
|
234
|
-
next =
|
|
235
|
-
...next,
|
|
236
|
-
blocks: keepLatestBlocks(capped),
|
|
237
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
238
|
-
footer: 'tool_running',
|
|
239
|
-
};
|
|
220
|
+
next = withCappedBlocks(next, capped, 'tool_running');
|
|
240
221
|
}
|
|
241
222
|
if (event.plan !== undefined) {
|
|
242
223
|
const newPlan = keepTailMarked(event.plan, MAX_REASONING_CHARS * 2);
|
|
@@ -256,12 +237,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
256
237
|
...(complete ? { completedAt: ts } : {}),
|
|
257
238
|
};
|
|
258
239
|
}, !complete);
|
|
259
|
-
next = {
|
|
260
|
-
...next,
|
|
261
|
-
plan: newPlan,
|
|
262
|
-
blocks: keepLatestBlocks(capped),
|
|
263
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
264
|
-
};
|
|
240
|
+
next = withCappedBlocks({ ...next, plan: newPlan }, capped);
|
|
265
241
|
}
|
|
266
242
|
if (event.fileChanges !== undefined) {
|
|
267
243
|
let blocks = next.blocks;
|
|
@@ -278,12 +254,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
278
254
|
// 同一 item 的同一路径原地替换(权威 diff 校正),新路径/新 item 追加。
|
|
279
255
|
blocks = upsertSnapshotBlock(blocks, (b) => b.kind === 'file_change' && b.itemId === event.itemId && b.path === fc.path, () => fileBlock, () => fileBlock, !complete);
|
|
280
256
|
}
|
|
281
|
-
|
|
282
|
-
next = {
|
|
283
|
-
...next,
|
|
284
|
-
blocks: keepLatestBlocks(capped),
|
|
285
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
286
|
-
};
|
|
257
|
+
next = withCappedBlocks(next, blocks);
|
|
287
258
|
}
|
|
288
259
|
return next;
|
|
289
260
|
}
|
|
@@ -313,8 +284,6 @@ function upsertSnapshotBlock(blocks, predicate, fallback, update, moveToEndWhenN
|
|
|
313
284
|
}
|
|
314
285
|
/** Handle system init and compact_boundary events. */
|
|
315
286
|
function reduceSystemEvent(state, event) {
|
|
316
|
-
if (event.type !== 'system')
|
|
317
|
-
return state;
|
|
318
287
|
if (event.subtype === 'init') {
|
|
319
288
|
return { ...state, sessionId: event.session_id, model: event.model, footer: 'thinking' };
|
|
320
289
|
}
|
|
@@ -336,8 +305,6 @@ function reduceSystemEvent(state, event) {
|
|
|
336
305
|
* - errorMsg: 首个非 undefined 胜出(agent 的更具体)
|
|
337
306
|
*/
|
|
338
307
|
function reduceResultEvent(state, event) {
|
|
339
|
-
if (event.type !== 'result')
|
|
340
|
-
return state;
|
|
341
308
|
// Pre-init result guard: Claude CLI --resume emits a historical result (from
|
|
342
309
|
// the previous turn) before sending system.init for the new run. Without this
|
|
343
310
|
// guard, that stale result would prematurely transition to 'finalizing',
|
|
@@ -375,8 +342,6 @@ function reduceResultEvent(state, event) {
|
|
|
375
342
|
}
|
|
376
343
|
/** Record an authoritative protocol final answer and remove its progress copy. */
|
|
377
344
|
function reduceFinalAnswerEvent(state, event) {
|
|
378
|
-
if (event.type !== 'final_answer')
|
|
379
|
-
return state;
|
|
380
345
|
if (!event.text.trim() && !state.finalAnswer)
|
|
381
346
|
return state;
|
|
382
347
|
const blocks = event.itemId
|
|
@@ -443,8 +408,6 @@ function promoteFinalAnswer(state, source, authoritativeText) {
|
|
|
443
408
|
* 不再走事件(那时已经没有 run 在消费流了)。
|
|
444
409
|
*/
|
|
445
410
|
function reduceBackgroundJobsEvent(state, event) {
|
|
446
|
-
if (event.type !== 'background_jobs')
|
|
447
|
-
return state;
|
|
448
411
|
return { ...state, backgroundJobs: event.jobs };
|
|
449
412
|
}
|
|
450
413
|
/**
|
|
@@ -460,6 +423,95 @@ export function removeBackgroundJob(state, jobId) {
|
|
|
460
423
|
backgroundJobs: (state.backgroundJobs ?? []).filter((job) => job.id !== jobId),
|
|
461
424
|
};
|
|
462
425
|
}
|
|
426
|
+
/** 宿主仍在执行的任务状态词。状态缺失也算在跑——它还在名单上。 */
|
|
427
|
+
const RUNNING_JOB_STATUSES = new Set(['running', 'stopping']);
|
|
428
|
+
/**
|
|
429
|
+
* 该任务是否仍在跑(`ended` / `unknown` 都不算)。
|
|
430
|
+
*
|
|
431
|
+
* 卡片据此决定要不要渲染「⏹ 停止」:给已经结束的任务留一个点了必然失败的
|
|
432
|
+
* 按钮,等于逼用户拿「停止」当查询用。
|
|
433
|
+
*/
|
|
434
|
+
export function isLiveBackgroundJob(job) {
|
|
435
|
+
return job.status === undefined || RUNNING_JOB_STATUSES.has(job.status);
|
|
436
|
+
}
|
|
437
|
+
/** 宿主状态 → 桥侧状态:仍在跑的保留原词,其余已知终态归一成 `ended`。 */
|
|
438
|
+
function bridgeJobStatus(status) {
|
|
439
|
+
return RUNNING_JOB_STATUSES.has(status) ? status : 'ended';
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* 收尾后把宿主推来的整份任务名单同步进终态卡状态。
|
|
443
|
+
*
|
|
444
|
+
* 与 reducer 的分工:reducer 处理的是"收尾那一刻的名单",只在 run 还在消费
|
|
445
|
+
* 事件时有效;终态之后 `reduceRunState` 直接早退(终态幂等),名单只能由
|
|
446
|
+
* 这里更新。
|
|
447
|
+
*
|
|
448
|
+
* 三条规则:
|
|
449
|
+
* - 名单里消失 = 宿主已结算(`job/list` 是整份替换,摘掉即结束)→ `ended`;
|
|
450
|
+
* - 名单里还在、宿主却给了终态词 → `ended`(有些宿主会把已结束的行留在名单里);
|
|
451
|
+
* - `ended` 是终态,不回退成 running —— 同一个 job id 不会重新开始跑。
|
|
452
|
+
*
|
|
453
|
+
* 无变化时返回**同一个 state 引用**:bridge 靠它跳过无谓的卡片 patch。
|
|
454
|
+
*/
|
|
455
|
+
export function syncBackgroundJobs(state, roster) {
|
|
456
|
+
const tracked = state.backgroundJobs ?? [];
|
|
457
|
+
const byId = new Map(roster.map((job) => [job.id, job]));
|
|
458
|
+
const next = tracked.map((job) => {
|
|
459
|
+
const row = byId.get(job.id);
|
|
460
|
+
if (!row)
|
|
461
|
+
return job.status === 'ended' ? job : { ...job, status: 'ended' };
|
|
462
|
+
if (job.status === 'ended')
|
|
463
|
+
return job;
|
|
464
|
+
const status = row.status === undefined ? job.status : bridgeJobStatus(row.status);
|
|
465
|
+
const label = row.label.trim() === '' ? job.label : row.label;
|
|
466
|
+
const progress = row.progress ?? job.progress;
|
|
467
|
+
if (status === job.status && label === job.label && progress === job.progress)
|
|
468
|
+
return job;
|
|
469
|
+
return {
|
|
470
|
+
...job,
|
|
471
|
+
label,
|
|
472
|
+
...(status === undefined ? {} : { status }),
|
|
473
|
+
...(progress === undefined ? {} : { progress }),
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
// 新出现的任务只收仍在跑的:与收尾采集同一口径(列出已结束的任务只会被
|
|
477
|
+
// 读成"还在跑")。
|
|
478
|
+
const seen = new Set(tracked.map((job) => job.id));
|
|
479
|
+
for (const row of roster) {
|
|
480
|
+
if (seen.has(row.id))
|
|
481
|
+
continue;
|
|
482
|
+
if (row.status !== undefined && !RUNNING_JOB_STATUSES.has(row.status))
|
|
483
|
+
continue;
|
|
484
|
+
next.push({
|
|
485
|
+
id: row.id,
|
|
486
|
+
label: row.label,
|
|
487
|
+
...(row.status === undefined ? {} : { status: row.status }),
|
|
488
|
+
...(row.progress === undefined ? {} : { progress: row.progress }),
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
if (next.length === tracked.length && next.every((job, index) => job === tracked[index])) {
|
|
492
|
+
return state;
|
|
493
|
+
}
|
|
494
|
+
return { ...state, backgroundJobs: next };
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* 订阅断了但任务仍未结算:把在跑的任务标成「状态未知」。
|
|
498
|
+
*
|
|
499
|
+
* 监控中断不等于任务结束——不标出来,卡片又会退化成"最后一次观测当现状",
|
|
500
|
+
* 正是这次要修的那个缺陷。未知态仍保留停止按钮(用户还能尝试停掉)。
|
|
501
|
+
*/
|
|
502
|
+
export function markBackgroundJobsUnknown(state) {
|
|
503
|
+
const jobs = state.backgroundJobs;
|
|
504
|
+
if (!jobs || jobs.length === 0)
|
|
505
|
+
return state;
|
|
506
|
+
let changed = false;
|
|
507
|
+
const next = jobs.map((job) => {
|
|
508
|
+
if (!isLiveBackgroundJob(job))
|
|
509
|
+
return job;
|
|
510
|
+
changed = true;
|
|
511
|
+
return { ...job, status: 'unknown' };
|
|
512
|
+
});
|
|
513
|
+
return changed ? { ...state, backgroundJobs: next } : state;
|
|
514
|
+
}
|
|
463
515
|
/**
|
|
464
516
|
* 运行期状态投影 → 状态行。
|
|
465
517
|
*
|
|
@@ -467,8 +519,6 @@ export function removeBackgroundJob(state, jobId) {
|
|
|
467
519
|
* 维度(上一步算出的上下文水位)擦掉。
|
|
468
520
|
*/
|
|
469
521
|
function reduceRunStatusEvent(state, event) {
|
|
470
|
-
if (event.type !== 'run_status')
|
|
471
|
-
return state;
|
|
472
522
|
const prev = state.liveStatus ?? {};
|
|
473
523
|
const live = {
|
|
474
524
|
...prev,
|
|
@@ -486,8 +536,6 @@ function reduceRunStatusEvent(state, event) {
|
|
|
486
536
|
}
|
|
487
537
|
/** Handle assistant thinking/text/tool_use content blocks. */
|
|
488
538
|
function reduceAssistantEvent(state, event) {
|
|
489
|
-
if (event.type !== 'assistant')
|
|
490
|
-
return state;
|
|
491
539
|
let next = state;
|
|
492
540
|
for (const content of event.message.content) {
|
|
493
541
|
if (content.type === 'thinking') {
|
|
@@ -516,12 +564,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
516
564
|
timestamp: event.timestamp,
|
|
517
565
|
},
|
|
518
566
|
];
|
|
519
|
-
next =
|
|
520
|
-
...next,
|
|
521
|
-
blocks: keepLatestBlocks(newBlocks),
|
|
522
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
523
|
-
footer: 'thinking',
|
|
524
|
-
};
|
|
567
|
+
next = withCappedBlocks(next, newBlocks, 'thinking');
|
|
525
568
|
}
|
|
526
569
|
else if (content.type === 'text') {
|
|
527
570
|
const blocks = markThinkingInactive(next.blocks);
|
|
@@ -545,12 +588,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
545
588
|
timestamp: event.timestamp,
|
|
546
589
|
},
|
|
547
590
|
];
|
|
548
|
-
next =
|
|
549
|
-
...next,
|
|
550
|
-
blocks: keepLatestBlocks(newBlocks),
|
|
551
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
552
|
-
footer: 'streaming',
|
|
553
|
-
};
|
|
591
|
+
next = withCappedBlocks(next, newBlocks, 'streaming');
|
|
554
592
|
}
|
|
555
593
|
else {
|
|
556
594
|
// parse the truncated stored string ONCE here (not on every render)
|
|
@@ -585,12 +623,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
585
623
|
}
|
|
586
624
|
else {
|
|
587
625
|
const capped = [...blocks, { kind: 'tool', tool: incomingTool }];
|
|
588
|
-
next =
|
|
589
|
-
...next,
|
|
590
|
-
blocks: keepLatestBlocks(capped),
|
|
591
|
-
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
592
|
-
footer: 'tool_running',
|
|
593
|
-
};
|
|
626
|
+
next = withCappedBlocks(next, capped, 'tool_running');
|
|
594
627
|
}
|
|
595
628
|
}
|
|
596
629
|
}
|
|
@@ -627,8 +660,6 @@ function mergeToolUse(existing, incoming) {
|
|
|
627
660
|
* advances to 'streaming' on every content block (parity with old behavior);
|
|
628
661
|
* only the blocks array is preserved by reference when nothing matches. */
|
|
629
662
|
function reduceToolResultEvent(state, event) {
|
|
630
|
-
if (event.type !== 'user')
|
|
631
|
-
return state;
|
|
632
663
|
let next = state;
|
|
633
664
|
for (const content of event.message.content) {
|
|
634
665
|
const targetId = content.tool_use_id;
|
|
@@ -656,8 +687,6 @@ function reduceToolResultEvent(state, event) {
|
|
|
656
687
|
}
|
|
657
688
|
/** Handle notice event — append a runtime notice (warning / reroute / compaction). */
|
|
658
689
|
function reduceNoticeEvent(state, event) {
|
|
659
|
-
if (event.type !== 'notice')
|
|
660
|
-
return state;
|
|
661
690
|
return {
|
|
662
691
|
...state,
|
|
663
692
|
notices: [
|
|
@@ -673,8 +702,6 @@ function reduceNoticeEvent(state, event) {
|
|
|
673
702
|
}
|
|
674
703
|
/** Handle session_info event — update sessionTitle / mode only when present. */
|
|
675
704
|
function reduceSessionInfoEvent(state, event) {
|
|
676
|
-
if (event.type !== 'session_info')
|
|
677
|
-
return state;
|
|
678
705
|
return {
|
|
679
706
|
...state,
|
|
680
707
|
...(event.title !== undefined ? { sessionTitle: event.title } : {}),
|
|
@@ -683,8 +710,6 @@ function reduceSessionInfoEvent(state, event) {
|
|
|
683
710
|
}
|
|
684
711
|
/** Handle plan event — accumulate plan text for Codex-style agents. */
|
|
685
712
|
function reducePlanEvent(state, event) {
|
|
686
|
-
if (event.type !== 'plan')
|
|
687
|
-
return state;
|
|
688
713
|
const currentPlan = state.plan ?? '';
|
|
689
714
|
const newPlan = keepTailMarked(currentPlan + '\n' + event.plan, MAX_REASONING_CHARS * 2);
|
|
690
715
|
// Plan is a single evolving document — replace the existing plan block
|
|
@@ -694,17 +719,10 @@ function reducePlanEvent(state, event) {
|
|
|
694
719
|
...withoutOldPlan,
|
|
695
720
|
{ kind: 'plan', content: newPlan, active: true, timestamp: event.timestamp },
|
|
696
721
|
];
|
|
697
|
-
return {
|
|
698
|
-
...state,
|
|
699
|
-
plan: newPlan,
|
|
700
|
-
blocks: keepLatestBlocks(newBlocks),
|
|
701
|
-
omittedBlocks: (state.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
702
|
-
};
|
|
722
|
+
return withCappedBlocks({ ...state, plan: newPlan }, newBlocks);
|
|
703
723
|
}
|
|
704
724
|
/** Handle file_change event — emit a file_change block for rendering (Codex-style agents). */
|
|
705
725
|
function reduceFileChangeEvent(state, event) {
|
|
706
|
-
if (event.type !== 'file_change')
|
|
707
|
-
return state;
|
|
708
726
|
// Add file_change block for rendering (collapsed by default)
|
|
709
727
|
const newBlocks = [
|
|
710
728
|
...state.blocks,
|
|
@@ -716,11 +734,7 @@ function reduceFileChangeEvent(state, event) {
|
|
|
716
734
|
timestamp: event.timestamp,
|
|
717
735
|
},
|
|
718
736
|
];
|
|
719
|
-
return
|
|
720
|
-
...state,
|
|
721
|
-
blocks: keepLatestBlocks(newBlocks),
|
|
722
|
-
omittedBlocks: (state.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
723
|
-
};
|
|
737
|
+
return withCappedBlocks(state, newBlocks);
|
|
724
738
|
}
|
|
725
739
|
export function finishRun(state, terminal, meta = {}) {
|
|
726
740
|
const withFinalAnswer = promoteFinalAnswer(state, 'inferred');
|
|
@@ -773,14 +787,27 @@ function stringifyUnknown(value) {
|
|
|
773
787
|
try {
|
|
774
788
|
// JSON.stringify(undefined) returns the value undefined, which would
|
|
775
789
|
// violate this function's string contract and crash callers that take
|
|
776
|
-
// .length on the result
|
|
777
|
-
// → truncateDetail TypeError). Normalize to ''.
|
|
790
|
+
// .length on the result. Normalize to ''.
|
|
778
791
|
return JSON.stringify(value) ?? '';
|
|
779
792
|
}
|
|
780
793
|
catch {
|
|
781
794
|
return String(value);
|
|
782
795
|
}
|
|
783
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* 尾块统一收口:keepLatestBlocks 截断 + omittedBlocks 计数 + footer 更新一步
|
|
799
|
+
* 完成。所有「追加/更新块后只保留最新 MAX_BLOCKS 块」的事件归约共用(此前
|
|
800
|
+
* 多处逐字重复);`blocks` 必须传截断前的数组,被丢出的最旧块才会计入
|
|
801
|
+
* omittedBlocks。`footer` 不传时保持原值(plan/file_change 归约不改 footer)。
|
|
802
|
+
*/
|
|
803
|
+
function withCappedBlocks(next, blocks, footer) {
|
|
804
|
+
return {
|
|
805
|
+
...next,
|
|
806
|
+
blocks: keepLatestBlocks(blocks),
|
|
807
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, blocks.length - MAX_BLOCKS),
|
|
808
|
+
...(footer === undefined ? {} : { footer }),
|
|
809
|
+
};
|
|
810
|
+
}
|
|
784
811
|
function keepLatestBlocks(blocks) {
|
|
785
812
|
return blocks.length <= MAX_BLOCKS ? blocks : blocks.slice(-MAX_BLOCKS);
|
|
786
813
|
}
|
|
@@ -790,8 +817,8 @@ const TRUNCATION_MARKER_RE = /^…(前 (\d+) 字符已省略)\n/;
|
|
|
790
817
|
* 给「已经不再是最新」的 text 块补上 `MAX_TEXT_CHARS` 存储窗口。
|
|
791
818
|
*
|
|
792
819
|
* 为什么最新一条不截:它就是卡片上的**输出**,终态还会被 `promoteFinalAnswer`
|
|
793
|
-
*
|
|
794
|
-
*
|
|
820
|
+
* 提成最终回答。提前按窗口切掉头部,渲染层再怎么回答优先也补不回来
|
|
821
|
+
* (16074 字符的回答只进到渲染层 12000 字符,且是从中间开始的)。
|
|
795
822
|
*
|
|
796
823
|
* 为什么其余 text 块仍然截:它们只是进度信息,卡片本来就只会展示最新几步;
|
|
797
824
|
* 存储层必须保持有界(`MAX_BLOCKS` × 窗口),否则长 run 的内存随历史线性增长。
|
|
@@ -826,7 +853,7 @@ function capStaleTextBlocks(blocks) {
|
|
|
826
853
|
* keepTail + 截断标记:被去头的存储内容以前缀如实标注(信息保真红线:禁止静默丢弃)。
|
|
827
854
|
* 标记计入 maxChars 预算(存储总量仍 ≤ maxChars,保持既有长度不变量)。
|
|
828
855
|
*
|
|
829
|
-
*
|
|
856
|
+
* 计数口径:只统计**真实丢弃的正文**,不含标记自身开销。
|
|
830
857
|
* 关键点是增量流下 `value` 往往已带上一次的标记(`marker + kept`);若直接
|
|
831
858
|
* 用 `value.length - kept.length` 计费,会把上一帧的标记文本也算进「已省略」,
|
|
832
859
|
* 造成每帧显示十几字符的虚高。这里先剥离旧标记,把 N 定义为**从该块起始累计
|
|
@@ -17,7 +17,7 @@ export { displayWidth, truncateMiddleToDisplayWidth, truncateToDisplayWidth, } f
|
|
|
17
17
|
* 手机(~390px 内容宽)上折叠面板的标题只有**一行**可用:减去图标与内边距约
|
|
18
18
|
* 320px,按 ~7.5px/半角算 ≈ 42 个半角宽度(iPhone SE 更窄,超出时按 `·`/空格
|
|
19
19
|
* 折行仍可读,但不能整段换行)。超过就折行,断点常常落在 URL 或命令
|
|
20
|
-
*
|
|
20
|
+
* 中间。完整参数在展开后的正文里,标题只负责"哪个工具、
|
|
21
21
|
* 动了什么",并带上 `HH:mm` 时间戳(一起计入预算)。
|
|
22
22
|
*/
|
|
23
23
|
export declare const HEADER_DISPLAY_MAX = 42;
|
package/dist/card/tool-render.js
CHANGED
|
@@ -20,7 +20,7 @@ export { displayWidth, truncateMiddleToDisplayWidth, truncateToDisplayWidth, } f
|
|
|
20
20
|
* 手机(~390px 内容宽)上折叠面板的标题只有**一行**可用:减去图标与内边距约
|
|
21
21
|
* 320px,按 ~7.5px/半角算 ≈ 42 个半角宽度(iPhone SE 更窄,超出时按 `·`/空格
|
|
22
22
|
* 折行仍可读,但不能整段换行)。超过就折行,断点常常落在 URL 或命令
|
|
23
|
-
*
|
|
23
|
+
* 中间。完整参数在展开后的正文里,标题只负责"哪个工具、
|
|
24
24
|
* 动了什么",并带上 `HH:mm` 时间戳(一起计入预算)。
|
|
25
25
|
*/
|
|
26
26
|
export const HEADER_DISPLAY_MAX = 42;
|
|
@@ -8,6 +8,18 @@
|
|
|
8
8
|
import type { UsageSnapshot } from '../session-contract.js';
|
|
9
9
|
/** `formatUsageStats` 只读这几项:会话/进度卡片上的紧凑用量行。 */
|
|
10
10
|
export type SessionDisplayUsage = Pick<UsageSnapshot, 'contextLength' | 'contextLimit' | 'compactCount' | 'cacheReadTokens' | 'cacheCreationTokens'>;
|
|
11
|
+
/**
|
|
12
|
+
* Format number to human-readable unit string.
|
|
13
|
+
* - >= 999_500: M with at most one decimal (e.g., 999_600 → 1M, 1200000 → 1.2M,
|
|
14
|
+
* 25000000 → 25M)
|
|
15
|
+
* - >= 1K: rounded to nearest integer K (e.g., 1500 → 2K, 120000 → 120K)
|
|
16
|
+
* - < 1K: as-is (e.g., 500 → "500")
|
|
17
|
+
*
|
|
18
|
+
* 千位边界取 M 档:999_500 起按 K 四舍五入会得到 "1000K",直接按 M 档展示。
|
|
19
|
+
* 此前 run-renderer 另有一份 formatTokens 只在这边带此边界护栏,两处已收敛
|
|
20
|
+
* 到本函数(run-renderer 的状态行 `📊 N` 也走这里)。
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatTokenK(n: number): string;
|
|
11
23
|
/**
|
|
12
24
|
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
13
25
|
* Each stat on its own line with blank lines between sections.
|
|
@@ -7,12 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
9
|
* Format number to human-readable unit string.
|
|
10
|
-
* - >=
|
|
10
|
+
* - >= 999_500: M with at most one decimal (e.g., 999_600 → 1M, 1200000 → 1.2M,
|
|
11
|
+
* 25000000 → 25M)
|
|
11
12
|
* - >= 1K: rounded to nearest integer K (e.g., 1500 → 2K, 120000 → 120K)
|
|
12
13
|
* - < 1K: as-is (e.g., 500 → "500")
|
|
14
|
+
*
|
|
15
|
+
* 千位边界取 M 档:999_500 起按 K 四舍五入会得到 "1000K",直接按 M 档展示。
|
|
16
|
+
* 此前 run-renderer 另有一份 formatTokens 只在这边带此边界护栏,两处已收敛
|
|
17
|
+
* 到本函数(run-renderer 的状态行 `📊 N` 也走这里)。
|
|
13
18
|
*/
|
|
14
|
-
function formatTokenK(n) {
|
|
15
|
-
if (n >=
|
|
19
|
+
export function formatTokenK(n) {
|
|
20
|
+
if (n >= 999_500) {
|
|
16
21
|
const m = n / 1_000_000;
|
|
17
22
|
const rounded = Math.round(m * 10) / 10;
|
|
18
23
|
// Drop unnecessary ".0" for whole numbers (2.0M → 2M)
|
package/dist/cli.js
CHANGED
|
@@ -92,10 +92,17 @@ export async function bootstrap(deps) {
|
|
|
92
92
|
forwarders.set(sig, forwarder);
|
|
93
93
|
onSignal(sig, forwarder);
|
|
94
94
|
}
|
|
95
|
-
child.on('error', () => {
|
|
95
|
+
child.on('error', (err) => {
|
|
96
|
+
// bun 拉起失败(ENOENT/EACCES)会静默切 node 兜底,但错误本身必须留痕,
|
|
97
|
+
// 否则「为什么跑在了 node 上」无从排查。logger 此时尚未初始化,console
|
|
98
|
+
// 是规则允许的启动期例外。
|
|
99
|
+
console.error('[lark-remote] bun spawn failed, falling back to node:', err);
|
|
96
100
|
for (const [sig, forwarder] of forwarders)
|
|
97
101
|
offSignal(sig, forwarder);
|
|
98
|
-
void importEntry().catch(() =>
|
|
102
|
+
void importEntry().catch((importErr) => {
|
|
103
|
+
console.error('[lark-remote] node fallback import failed:', importErr);
|
|
104
|
+
exit(1);
|
|
105
|
+
});
|
|
99
106
|
});
|
|
100
107
|
child.on('exit', (code, signal) => {
|
|
101
108
|
if (signal) {
|
|
@@ -146,7 +153,12 @@ function main() {
|
|
|
146
153
|
killSelf: (sig) => process.kill(process.pid, sig),
|
|
147
154
|
exit: (code) => process.exit(code),
|
|
148
155
|
};
|
|
149
|
-
|
|
156
|
+
// bootstrap 是第一接触面:静默 exit(1) 会让安装损坏/依赖缺失表现为零输出
|
|
157
|
+
// 退出,必须把根因打到 stderr(logger 未初始化,console 为规则允许的例外)。
|
|
158
|
+
void bootstrap(deps).catch((err) => {
|
|
159
|
+
console.error('[lark-remote] bootstrap failed:', err);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
|
150
162
|
}
|
|
151
163
|
if (isDirectEntry()) {
|
|
152
164
|
main();
|
package/dist/clone.js
CHANGED
|
@@ -237,7 +237,7 @@ export class CloneSession {
|
|
|
237
237
|
// 图片发送失败不阻断流程:给出浏览器打开兜底。
|
|
238
238
|
await this.connector
|
|
239
239
|
.sendWithRetry(ctx.chatId, { text: `⚠️ 二维码图片发送失败,请在浏览器打开创建:${info.url}` }, { replyTo: ctx.messageId })
|
|
240
|
-
.catch(() =>
|
|
240
|
+
.catch((err) => getLogger().warn('[clone] fallback notice send failed:', err));
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
243
|
finally {
|
|
@@ -336,7 +336,7 @@ export class CloneSession {
|
|
|
336
336
|
`新应用凭证(请妥善保管):App ID ${result.client_id}\n` +
|
|
337
337
|
'可修复本机问题后用 /clone 重新走一遍流程(会创建新应用)',
|
|
338
338
|
}, { replyTo: ctx.messageId })
|
|
339
|
-
.catch(() =>
|
|
339
|
+
.catch((sendErr) => getLogger().warn('[clone] finalize failure notice send failed:', sendErr));
|
|
340
340
|
}
|
|
341
341
|
finally {
|
|
342
342
|
this.state = 'idle';
|