lark-remote 0.5.6 → 0.5.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 +46 -6
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/dist/bridge/index.d.ts +63 -1
- package/dist/bridge/index.js +147 -14
- package/dist/bridge/queue-manager.js +41 -58
- package/dist/card/approval-render.js +2 -1
- package/dist/card/bash-card-session.d.ts +0 -1
- package/dist/card/bash-card-session.js +0 -8
- package/dist/card/bash-renderer.js +2 -1
- package/dist/card/card-budget.d.ts +37 -0
- package/dist/card/card-budget.js +143 -24
- package/dist/card/card-session.d.ts +3 -13
- package/dist/card/card-session.js +4 -18
- package/dist/card/card-shared.d.ts +11 -0
- package/dist/card/card-shared.js +19 -13
- package/dist/card/run-card-session.d.ts +0 -1
- package/dist/card/run-card-session.js +0 -6
- package/dist/card/run-renderer.d.ts +18 -0
- package/dist/card/run-renderer.js +330 -62
- package/dist/card/run-state.d.ts +26 -0
- package/dist/card/run-state.js +45 -0
- package/dist/card/text-truncate.d.ts +3 -5
- package/dist/card/text-truncate.js +3 -5
- package/dist/card/time.d.ts +14 -0
- package/dist/card/time.js +30 -0
- package/dist/card/tool-render.d.ts +15 -2
- package/dist/card/tool-render.js +42 -82
- package/dist/common/text-width.d.ts +24 -0
- package/dist/common/text-width.js +93 -0
- package/dist/common/tool-summary.d.ts +19 -0
- package/dist/common/tool-summary.js +171 -0
- package/dist/common/turn-failure.d.ts +13 -0
- package/dist/common/turn-failure.js +18 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -1
- package/dist/connector/index.js +1 -1
- package/dist/index.js +66 -13
- package/dist/order/index.d.ts +2 -0
- package/dist/order/index.js +2 -0
- package/dist/platform/command.js +1 -1
- package/dist/platform/shell.d.ts +2 -2
- package/dist/platform/spawn.d.ts +1 -1
- package/dist/platform/spawn.js +2 -2
- package/dist/platform/types.d.ts +1 -1
- package/dist/router/card-helpers.d.ts +18 -16
- package/dist/router/card-helpers.js +88 -46
- package/dist/router/config/codex.js +2 -3
- package/dist/router/config/common/model-patch.d.ts +8 -0
- package/dist/router/config/common/model-patch.js +13 -0
- package/dist/router/config/common/render.js +30 -77
- package/dist/router/config/dsh.js +2 -1
- package/dist/router/config/kimi.js +3 -3
- package/dist/router/index.d.ts +9 -11
- package/dist/router/index.js +165 -356
- package/dist/router/order-exec-dispatch.js +2 -1
- package/dist/runner/claude/session.d.ts +1 -1
- package/dist/runner/claude/session.js +3 -3
- package/dist/runner/common/defaults.d.ts +2 -0
- package/dist/runner/common/defaults.js +3 -0
- package/dist/runner/common/jsonrpc/connection-manager.js +1 -1
- package/dist/runner/common/jsonrpc/transport.d.ts +1 -1
- package/dist/runner/common/jsonrpc/transport.js +3 -3
- package/dist/runner/common/spawning-runner.d.ts +1 -1
- package/dist/runner/common/spawning-runner.js +2 -2
- package/dist/runner/dsh/auth.js +2 -1
- package/dist/runner/dsh/client.d.ts +100 -3
- package/dist/runner/dsh/client.js +290 -6
- package/dist/runner/dsh/exit-cancel.d.ts +32 -0
- package/dist/runner/dsh/exit-cancel.js +72 -0
- package/dist/runner/dsh/inflight.d.ts +62 -0
- package/dist/runner/dsh/inflight.js +140 -0
- package/dist/runner/dsh/runner.d.ts +90 -4
- package/dist/runner/dsh/runner.js +436 -18
- package/dist/runner/dsh/translator.d.ts +1 -1
- package/dist/runner/dsh/translator.js +103 -1
- package/dist/runner/dsh/types.d.ts +60 -0
- package/dist/runner/types.d.ts +95 -2
- package/dist/runner/types.js +9 -0
- package/dist/session/claude/session-index.d.ts +0 -4
- package/dist/session/claude/session-index.js +0 -8
- package/dist/session/claude/sessions.js +2 -2
- package/dist/session/dsh/sessions.js +157 -17
- package/dist/update/install.js +1 -1
- package/package.json +1 -1
|
@@ -24,6 +24,7 @@ import { dshArgs, extractDshToken } from '../../runner/dsh/client.js';
|
|
|
24
24
|
import { DSH_COOKIE_LIFETIME_MS, isDshCredentialsNotFound, mintDshBrowserCookie, readDshBrowserSecretSync, resolveDshCredentialsFile, } from '../../runner/dsh/auth.js';
|
|
25
25
|
import { capEvents, paginate } from '../common/pagination.js';
|
|
26
26
|
import { truncateUtf8, TOOL_RESULT_MAX_BYTES } from '../../common/truncate.js';
|
|
27
|
+
import { summarizeToolInput } from '../../common/tool-summary.js';
|
|
27
28
|
/**
|
|
28
29
|
* history 拉取上限。DSH session/page 的 maxMessages 同时限制返回条数,
|
|
29
30
|
* 若用它同时充当展示截断,>50 事件的会话累计 usage 会少算。这里拉取一个足够大的
|
|
@@ -171,9 +172,9 @@ export class DshSessionReader {
|
|
|
171
172
|
.filter((i) => i.cwd !== undefined && samePath(i.cwd, cwd))
|
|
172
173
|
.map((i) => ({
|
|
173
174
|
sessionId: String(i.sessionId),
|
|
174
|
-
summary: '',
|
|
175
175
|
mtime: i.updatedAt,
|
|
176
176
|
...sessionPreset(i),
|
|
177
|
+
...sessionPreview(i),
|
|
177
178
|
}));
|
|
178
179
|
sortByRecencyDesc(filtered, (i) => i.mtime, (i) => i.sessionId);
|
|
179
180
|
const { items: page, total } = paginate(filtered, opts ?? {});
|
|
@@ -199,6 +200,8 @@ export class DshSessionReader {
|
|
|
199
200
|
return { events: [] };
|
|
200
201
|
}
|
|
201
202
|
const throughSeq = this.resolveThroughSeq(sessionId, session);
|
|
203
|
+
// 服务端投影带来的标题与末轮输入:详情卡头部用它们,不必再从事件里猜。
|
|
204
|
+
const preview = sessionPreview(session);
|
|
202
205
|
const value = this.unary('session/page', {
|
|
203
206
|
address: { kind: 'session', sessionId },
|
|
204
207
|
throughSeq,
|
|
@@ -218,6 +221,8 @@ export class DshSessionReader {
|
|
|
218
221
|
// null = 历史里没有 turn 标记(旧数据/截断)→ 退回末条 usage 语义。
|
|
219
222
|
let turnUsage = null;
|
|
220
223
|
let lastTurnUsage = null;
|
|
224
|
+
/** 等结果的工具调用(见 MappedEvent.pendingTool)。 */
|
|
225
|
+
let pendingTool;
|
|
221
226
|
for (const entry of value.records ?? []) {
|
|
222
227
|
const ev = entry.event;
|
|
223
228
|
if (!ev)
|
|
@@ -231,7 +236,30 @@ export class DshSessionReader {
|
|
|
231
236
|
turnUsage = null;
|
|
232
237
|
}
|
|
233
238
|
const contentEvents = mapEvent(ev);
|
|
234
|
-
|
|
239
|
+
if (contentEvents.pendingTool) {
|
|
240
|
+
// 未完成的工具调用最多同时一个(DSH 按 step 串行执行;并行调用时按
|
|
241
|
+
// 到达顺序配对,最坏情况是两次调用共用一个面板,不会丢信息)。
|
|
242
|
+
if (pendingTool)
|
|
243
|
+
events.push(pendingTool);
|
|
244
|
+
pendingTool = contentEvents.pendingTool;
|
|
245
|
+
}
|
|
246
|
+
else if (ev.type === 'tool/result' && pendingTool && contentEvents.events[0]) {
|
|
247
|
+
const result = contentEvents.events[0];
|
|
248
|
+
// 标题沿用调用的 `🔧 name — summary`,失败改成 ❌(折叠态一眼看出结果)。
|
|
249
|
+
const failed = result.title?.startsWith('🔴') === true;
|
|
250
|
+
events.push({
|
|
251
|
+
type: 'tool_use',
|
|
252
|
+
title: failed
|
|
253
|
+
? (pendingTool.title ?? '❌ 工具').replace('🔧', '❌')
|
|
254
|
+
: pendingTool.title,
|
|
255
|
+
content: result.content,
|
|
256
|
+
timestamp: pendingTool.timestamp,
|
|
257
|
+
});
|
|
258
|
+
pendingTool = undefined;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
events.push(...contentEvents.events);
|
|
262
|
+
}
|
|
235
263
|
if (contentEvents.title)
|
|
236
264
|
displayTitle = contentEvents.title;
|
|
237
265
|
const u = contentEvents.usage;
|
|
@@ -278,12 +306,19 @@ export class DshSessionReader {
|
|
|
278
306
|
}),
|
|
279
307
|
};
|
|
280
308
|
}
|
|
309
|
+
// 中断/仍在执行的调用没有结果:把它当独立事件留下,别让这一条消失。
|
|
310
|
+
if (pendingTool)
|
|
311
|
+
events.push(pendingTool);
|
|
312
|
+
// 头部信息口径:最近输入优先用服务端末轮 prompt(事件里可能只是当前 turn
|
|
313
|
+
// 的一部分),标题用服务端 title;两者都缺时保持原行为。
|
|
314
|
+
const headerLastInput = preview.lastUserMessage ?? displayTitle;
|
|
281
315
|
return {
|
|
282
316
|
// 展示仍按 maxEvents 尾截断(默认 50,保持原行为),usage 已在上面基于
|
|
283
317
|
// 完整 history 累加。
|
|
284
318
|
events: capEvents(events, opts?.maxEvents ?? 50),
|
|
285
319
|
...(sessionUsage ? { usage: sessionUsage } : {}),
|
|
286
|
-
...(
|
|
320
|
+
...(headerLastInput ? { displayTitle: headerLastInput } : {}),
|
|
321
|
+
...(preview.title ? { aiTitle: preview.title } : {}),
|
|
287
322
|
};
|
|
288
323
|
}
|
|
289
324
|
catch (error) {
|
|
@@ -324,9 +359,77 @@ function sessionPreset(item) {
|
|
|
324
359
|
const preset = item.projections?.values?.agentPreset ?? item.agentPreset;
|
|
325
360
|
return preset ? { agentPreset: preset } : {};
|
|
326
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* 会话预览(列表卡上的「标题 / 最近输入 / 最后输出」)。
|
|
364
|
+
*
|
|
365
|
+
* 全部取自 `session/list` 的 `projections.values`——DSH 0.2.x 已经随列表一起
|
|
366
|
+
* 返回 title 与 turnOutline(每轮的 prompt/response),所以这里不需要再拉
|
|
367
|
+
* `session/page`:那是一次上万条事件的大响应,一页 5 个会话就是 5 次。
|
|
368
|
+
*
|
|
369
|
+
* `turnOutline` 的 prompt/response 由 DSH 侧按长度截断(结尾带 `…`),列表卡
|
|
370
|
+
* 只做单行预览,直接复用即可。缺字段 / 类型不对时全部留空,不影响列表。
|
|
371
|
+
*/
|
|
372
|
+
function sessionPreview(item) {
|
|
373
|
+
const values = item.projections?.values;
|
|
374
|
+
const rawTitle = values?.title;
|
|
375
|
+
const title = typeof rawTitle === 'string' ? rawTitle.trim() : '';
|
|
376
|
+
const outline = values?.turnOutline;
|
|
377
|
+
const lastUserMessage = lastOutlineText(outline, 'prompt');
|
|
378
|
+
const lastAssistantMessage = lastOutlineText(outline, 'response');
|
|
379
|
+
return {
|
|
380
|
+
// summary 同时填标题:只认 summary 的消费方(旧卡片路径、其他 agent 的
|
|
381
|
+
// 统一逻辑)也能拿到可读文本,不必区分 agent。
|
|
382
|
+
summary: title,
|
|
383
|
+
...(title ? { title } : {}),
|
|
384
|
+
...(lastUserMessage ? { lastUserMessage } : {}),
|
|
385
|
+
...(lastAssistantMessage ? { lastAssistantMessage } : {}),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 从 turnOutline 末条往回找第一个非空文本。
|
|
390
|
+
*
|
|
391
|
+
* 在途 turn 已经写进 outline,但 `response` 还是空串(实测末条
|
|
392
|
+
* `{turn:6, prompt:"…", response:""}`);直接取末条会让「最后输出」空掉,
|
|
393
|
+
* 所以回溯到最近一条有内容的结果。
|
|
394
|
+
*/
|
|
395
|
+
function lastOutlineText(outline, key) {
|
|
396
|
+
if (!Array.isArray(outline))
|
|
397
|
+
return undefined;
|
|
398
|
+
for (let i = outline.length - 1; i >= 0; i -= 1) {
|
|
399
|
+
const entry = outline[i];
|
|
400
|
+
if (!entry || typeof entry !== 'object')
|
|
401
|
+
continue;
|
|
402
|
+
const raw = entry[key];
|
|
403
|
+
if (typeof raw !== 'string')
|
|
404
|
+
continue;
|
|
405
|
+
const text = raw.trim();
|
|
406
|
+
if (text)
|
|
407
|
+
return text;
|
|
408
|
+
}
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
327
411
|
function emptyUsageTotals() {
|
|
328
412
|
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
329
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* `user/message.source.kind` 里属于**纯上下文注入**的种类:没有回顾价值,且
|
|
416
|
+
* 会挤掉真正的对话(5 条事件的窗口里被它们占满就什么都看不到了)。
|
|
417
|
+
*/
|
|
418
|
+
const CONTEXT_INJECTION_KINDS = new Set([
|
|
419
|
+
'time-context',
|
|
420
|
+
'agent-instructions',
|
|
421
|
+
'runtime-context',
|
|
422
|
+
'skill-catalog',
|
|
423
|
+
'compact-checkpoint',
|
|
424
|
+
]);
|
|
425
|
+
/** 系统通知(后台任务完成、目标变更)在回顾卡里的正文上限。 */
|
|
426
|
+
const NOTICE_MAX_BYTES = 400;
|
|
427
|
+
/** 单条思考在 resume 卡里的上限:过程信息,留头几百字即可。 */
|
|
428
|
+
const REASONING_MAX_BYTES = 800;
|
|
429
|
+
/** 单条 assistant 正文上限(最终答复通常最重要,给足空间)。 */
|
|
430
|
+
const TEXT_MAX_BYTES = 2_000;
|
|
431
|
+
/** 工具原始参数上限(标题已有摘要,正文只用于看完整命令)。 */
|
|
432
|
+
const TOOL_INPUT_MAX_BYTES = 600;
|
|
330
433
|
/** Map one DSH SessionEvent to display content events (+ title/usage side effects). */
|
|
331
434
|
function mapEvent(ev) {
|
|
332
435
|
const out = { events: [] };
|
|
@@ -337,42 +440,79 @@ function mapEvent(ev) {
|
|
|
337
440
|
.filter((c) => c.type === 'text')
|
|
338
441
|
.map((c) => c.text ?? '')
|
|
339
442
|
.join('');
|
|
340
|
-
|
|
443
|
+
if (!text.trim())
|
|
444
|
+
break;
|
|
445
|
+
// DSH 把上下文注入也写成 user/message,靠 `source.kind` 区分。真实会话里
|
|
446
|
+
// 30 条 user/message 中只有 8 条是用户本人说的(其余是 time-context /
|
|
447
|
+
// agent-instructions / skill-catalog / goal / tool-jobs…)——照单全收会让
|
|
448
|
+
// 回顾卡里塞满"Time sampled while preparing turn…",还把系统提示标成 👤 你。
|
|
449
|
+
const source = ev.data.source;
|
|
450
|
+
const kind = typeof source?.kind === 'string' ? source.kind : undefined;
|
|
451
|
+
if (kind && CONTEXT_INJECTION_KINDS.has(kind))
|
|
452
|
+
break;
|
|
453
|
+
const isNotice = kind !== undefined && kind !== 'user';
|
|
454
|
+
out.events.push({
|
|
455
|
+
type: isNotice ? 'notice' : 'user',
|
|
456
|
+
...(isNotice ? { title: '🔔 通知' } : {}),
|
|
457
|
+
content: isNotice ? truncateUtf8(text.trim(), NOTICE_MAX_BYTES) : text,
|
|
458
|
+
timestamp: new Date(ev.time).toISOString(),
|
|
459
|
+
});
|
|
341
460
|
break;
|
|
342
461
|
}
|
|
343
462
|
case 'assistant/message': {
|
|
344
463
|
const content = ev.data.message
|
|
345
464
|
?.content ?? [];
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
465
|
+
const timestamp = new Date(ev.time).toISOString();
|
|
466
|
+
// DSH 的块类型是 text / reasoning / tool-call(不是 Anthropic 的
|
|
467
|
+
// thinking)。只认 text 会让 resume 卡"只有工具调用、没有思考与输出"
|
|
468
|
+
// (2026-10-19 反馈:一个 31 turn 的会话里 reasoning 块 371 个、text 12 个,
|
|
469
|
+
// 全被丢掉)。tool-call 块不在这里取——独立的 `tool/call` 事件带完整参数。
|
|
470
|
+
for (const block of content) {
|
|
471
|
+
const text = typeof block.text === 'string' ? block.text.trim() : '';
|
|
472
|
+
if (!text)
|
|
473
|
+
continue;
|
|
474
|
+
if (block.type === 'text') {
|
|
475
|
+
out.events.push({
|
|
476
|
+
type: 'assistant',
|
|
477
|
+
content: truncateUtf8(text, TEXT_MAX_BYTES),
|
|
478
|
+
timestamp,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
else if (block.type === 'reasoning') {
|
|
482
|
+
out.events.push({
|
|
483
|
+
type: 'thinking',
|
|
484
|
+
// 思考是过程信息:resume 卡里留头几百字足够判断"当时在想什么"。
|
|
485
|
+
content: truncateUtf8(text, REASONING_MAX_BYTES),
|
|
486
|
+
timestamp,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
356
490
|
const usage = ev.data.usage;
|
|
357
491
|
if (usage && typeof usage === 'object')
|
|
358
492
|
out.usage = usage;
|
|
359
493
|
break;
|
|
360
494
|
}
|
|
361
495
|
case 'tool/call': {
|
|
496
|
+
// 调用本身先不产出事件:DSH 的 tool/call 与 tool/result 是一对,resume 卡
|
|
497
|
+
// 里合成一个面板(`🔧 bash — cmd` + 输出),而不是两个各占一屏。
|
|
362
498
|
const name = String(ev.data.name ?? '');
|
|
363
499
|
const args = typeof ev.data.arguments === 'string' ? ev.data.arguments : '';
|
|
364
|
-
|
|
500
|
+
const summary = summarizeToolInput(name, args);
|
|
501
|
+
out.pendingTool = {
|
|
365
502
|
type: 'tool_use',
|
|
366
|
-
|
|
503
|
+
title: summary ? `🔧 ${name} — ${summary}` : `🔧 ${name}`,
|
|
504
|
+
content: truncateUtf8(args, TOOL_INPUT_MAX_BYTES),
|
|
367
505
|
timestamp: new Date(ev.time).toISOString(),
|
|
368
|
-
}
|
|
506
|
+
};
|
|
369
507
|
break;
|
|
370
508
|
}
|
|
371
509
|
case 'tool/result': {
|
|
372
510
|
const msg = ev.data.message;
|
|
373
511
|
const raw = stringifyContent(msg?.content);
|
|
512
|
+
const isError = msg?.isError === true;
|
|
374
513
|
out.events.push({
|
|
375
514
|
type: 'tool_result',
|
|
515
|
+
title: isError ? '🔴 结果(失败)' : '🟢 结果',
|
|
376
516
|
content: truncateUtf8(raw, TOOL_RESULT_MAX_BYTES),
|
|
377
517
|
timestamp: new Date(ev.time).toISOString(),
|
|
378
518
|
});
|
package/dist/update/install.js
CHANGED
|
@@ -102,7 +102,7 @@ export function detectPackageManager(scriptPath) {
|
|
|
102
102
|
return inferred;
|
|
103
103
|
}
|
|
104
104
|
// 3. availability fallback: 纯 Node PATH 解析(win32 PATHEXT),不再依赖外部 which
|
|
105
|
-
// (Windows 无 which);win32 再用 where.exe
|
|
105
|
+
// (Windows 无 which);win32 再用 where.exe 兜底罕见的解析失败场景
|
|
106
106
|
const candidates = ['npm', 'bun', 'pnpm'];
|
|
107
107
|
for (const cmd of candidates) {
|
|
108
108
|
if (resolveExecutable(cmd))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|