lark-remote 0.5.7 → 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.
Files changed (47) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/bridge/index.d.ts +15 -0
  3. package/dist/bridge/index.js +61 -0
  4. package/dist/bridge/queue-manager.js +41 -58
  5. package/dist/card/approval-render.js +2 -1
  6. package/dist/card/bash-card-session.d.ts +0 -1
  7. package/dist/card/bash-card-session.js +0 -8
  8. package/dist/card/bash-renderer.js +2 -1
  9. package/dist/card/card-budget.js +15 -19
  10. package/dist/card/card-session.d.ts +3 -13
  11. package/dist/card/card-session.js +4 -18
  12. package/dist/card/card-shared.d.ts +11 -0
  13. package/dist/card/card-shared.js +19 -13
  14. package/dist/card/run-card-session.d.ts +0 -1
  15. package/dist/card/run-card-session.js +0 -6
  16. package/dist/card/run-renderer.d.ts +3 -9
  17. package/dist/card/run-renderer.js +99 -27
  18. package/dist/card/text-truncate.d.ts +3 -5
  19. package/dist/card/text-truncate.js +3 -5
  20. package/dist/card/tool-render.js +7 -21
  21. package/dist/common/tool-summary.d.ts +8 -0
  22. package/dist/common/tool-summary.js +38 -20
  23. package/dist/common/turn-failure.d.ts +13 -0
  24. package/dist/common/turn-failure.js +18 -0
  25. package/dist/index.js +5 -7
  26. package/dist/order/index.d.ts +2 -0
  27. package/dist/order/index.js +2 -0
  28. package/dist/router/card-helpers.js +23 -42
  29. package/dist/router/config/codex.js +2 -3
  30. package/dist/router/config/common/model-patch.d.ts +8 -0
  31. package/dist/router/config/common/model-patch.js +13 -0
  32. package/dist/router/config/common/render.js +30 -77
  33. package/dist/router/config/dsh.js +2 -1
  34. package/dist/router/config/kimi.js +3 -3
  35. package/dist/router/index.d.ts +0 -11
  36. package/dist/router/index.js +108 -349
  37. package/dist/router/order-exec-dispatch.js +2 -1
  38. package/dist/runner/common/defaults.d.ts +2 -0
  39. package/dist/runner/common/defaults.js +3 -0
  40. package/dist/runner/common/jsonrpc/connection-manager.js +1 -1
  41. package/dist/runner/dsh/client.js +1 -2
  42. package/dist/runner/types.d.ts +19 -0
  43. package/dist/runner/types.js +9 -0
  44. package/dist/session/claude/session-index.d.ts +0 -4
  45. package/dist/session/claude/session-index.js +0 -8
  46. package/dist/session/dsh/sessions.js +57 -2
  47. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { ORDER_NOT_FOUND_TEXT } from '../order/index.js';
1
2
  /**
2
3
  * Dispatch an `order.exec` card action as an equivalent queued user message
3
4
  * (Plan A): resolve the order text at the enqueue boundary, then enqueue it
@@ -20,7 +21,7 @@ export async function dispatchOrderExecForQueue(args) {
20
21
  }
21
22
  const resolved = router.resolveOrderExecForQueue(orderId);
22
23
  if (!resolved) {
23
- await bridge.sendResult({ text: '指令不存在或已被删除' }, ctx);
24
+ await bridge.sendResult({ text: ORDER_NOT_FOUND_TEXT }, ctx);
24
25
  return 'not-found';
25
26
  }
26
27
  // Step4/入队时刻快照 agent+session,随任务闭包带到执行时刻,
@@ -0,0 +1,2 @@
1
+ /** 默认单请求超时(JSON-RPC 与 DSH HTTP client 共用)。 */
2
+ export declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
@@ -0,0 +1,3 @@
1
+ /** 默认单请求超时(JSON-RPC 与 DSH HTTP client 共用)。 */
2
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
3
+ //# sourceMappingURL=defaults.js.map
@@ -17,8 +17,8 @@ import { JsonlRpcTransport } from './transport.js';
17
17
  import { JsonRpcClient } from './client.js';
18
18
  import { getLogger } from '../../../logger/index.js';
19
19
  import { agentStopperRegistry, } from '../../../platform/agent-stopper.js';
20
+ import { DEFAULT_REQUEST_TIMEOUT_MS } from '../defaults.js';
20
21
  const DEFAULT_IDLE_TTL_MS = 30 * 60 * 1000; // 30 minutes
21
- const DEFAULT_REQUEST_TIMEOUT_MS = 60_000; // 60 seconds
22
22
  /** acquire() 的轮次上限:防止 slot 被持续释放时无限重建连接。 */
23
23
  const MAX_ACQUIRE_ATTEMPTS = 5;
24
24
  export class ConnectionManager {
@@ -23,6 +23,7 @@ import { randomUUID } from 'node:crypto';
23
23
  import WebSocket from 'ws';
24
24
  import { getLogger } from '../../logger/index.js';
25
25
  import { DSH_COOKIE_LIFETIME_MS, isDshCredentialsNotFound, mintDshBrowserCookie, readDshBrowserSecret, resolveDshCredentialsFile, } from './auth.js';
26
+ import { DEFAULT_REQUEST_TIMEOUT_MS } from '../common/defaults.js';
26
27
  function defaultWebSocketFactory(url, options) {
27
28
  const socket = new WebSocket(url, {
28
29
  ...(options?.headers ? { headers: options.headers } : {}),
@@ -105,8 +106,6 @@ function inboxItemsOf(values) {
105
106
  }
106
107
  return out;
107
108
  }
108
- /** Default unary timeout: long prompts are unbounded on the server side. */
109
- const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
110
109
  /** Extract the `token` query parameter from a pasted `dsh web` URL. */
111
110
  export function extractDshToken(raw) {
112
111
  const value = (raw ?? '').trim();
@@ -507,6 +507,8 @@ export interface Runner {
507
507
  */
508
508
  export declare const AGENT_KINDS: readonly ["claude", "codex", "opencode", "pi", "kimi", "dsh"];
509
509
  export type AgentKind = (typeof AGENT_KINDS)[number];
510
+ /** 用户可见 agent 显示名单源:启动注册与无 registry 的回退路径共用。 */
511
+ export declare const AGENT_DISPLAY_NAMES: Record<AgentKind, string>;
510
512
  /**
511
513
  * Compact 能力声明(运行时门控的能力清单,编译期强制完整)。
512
514
  *
@@ -529,6 +531,23 @@ export interface AgentSession {
529
531
  mtime: number;
530
532
  /** DSH:session 创建时固定的 preset;其他 agent 无此概念(undefined)。 */
531
533
  agentPreset?: string;
534
+ /**
535
+ * 服务端投影已有的会话标题(DSH `projections.values.title`)。
536
+ *
537
+ * 与 `summary` 的区别:`summary` 是「首条用户输入」的兜底口径(claude /
538
+ * codex / pi / kimi 无标题字段),`title` 是 agent 自己起的标题。列表卡
539
+ * 用它决定标签文案(标题 / 会话摘要),缺省时回退 `summary`。
540
+ */
541
+ title?: string;
542
+ /**
543
+ * 最后一轮用户输入(服务端投影,单行预览)。
544
+ *
545
+ * 只给「列表响应里已顺带返回」的 reader 用(DSH 的 `turnOutline` 末条),
546
+ * 目的是让列表卡不必为每行做一次全量内容读取。
547
+ */
548
+ lastUserMessage?: string;
549
+ /** 最后一轮助手输出(服务端投影已截断,单行预览)。 */
550
+ lastAssistantMessage?: string;
532
551
  }
533
552
  /** A single renderable event from session history. */
534
553
  export interface AgentSessionContentEvent {
@@ -16,6 +16,15 @@
16
16
  * 新增 agent 只改这里。
17
17
  */
18
18
  export const AGENT_KINDS = ['claude', 'codex', 'opencode', 'pi', 'kimi', 'dsh'];
19
+ /** 用户可见 agent 显示名单源:启动注册与无 registry 的回退路径共用。 */
20
+ export const AGENT_DISPLAY_NAMES = {
21
+ claude: 'Claude',
22
+ codex: 'Codex',
23
+ opencode: 'Opencode',
24
+ pi: 'Pi',
25
+ kimi: 'Kimi',
26
+ dsh: 'DSH',
27
+ };
19
28
  /**
20
29
  * Compact 能力声明(运行时门控的能力清单,编译期强制完整)。
21
30
  *
@@ -74,10 +74,6 @@ export declare class SessionIndex {
74
74
  private readonly byCwd;
75
75
  private lastRefreshAt;
76
76
  private built;
77
- /** Test-only: count of parseSessionJsonl calls since creation */
78
- parseCount: number;
79
- /** Test-only: count of directory scan operations */
80
- scanCount: number;
81
77
  constructor(projectsDir: string, opts?: SessionIndexOptions);
82
78
  /** Whether a full scan has ever completed successfully (readdir succeeded). */
83
79
  get isBuilt(): boolean;
@@ -146,10 +146,6 @@ export class SessionIndex {
146
146
  byCwd = new Map();
147
147
  lastRefreshAt = 0;
148
148
  built = false;
149
- /** Test-only: count of parseSessionJsonl calls since creation */
150
- parseCount = 0;
151
- /** Test-only: count of directory scan operations */
152
- scanCount = 0;
153
149
  constructor(projectsDir, opts = {}) {
154
150
  this.projectsDir = projectsDir;
155
151
  this.refreshIntervalMs = opts.refreshIntervalMs ?? 5000;
@@ -161,7 +157,6 @@ export class SessionIndex {
161
157
  }
162
158
  // ── Build (initial full scan) ───────────────────────────
163
159
  build() {
164
- this.scanCount++;
165
160
  this.fullScan();
166
161
  this.lastRefreshAt = Date.now();
167
162
  }
@@ -171,7 +166,6 @@ export class SessionIndex {
171
166
  if (now - this.lastRefreshAt < this.refreshIntervalMs) {
172
167
  return; // throttled
173
168
  }
174
- this.scanCount++;
175
169
  this.fullScan();
176
170
  this.lastRefreshAt = now;
177
171
  }
@@ -232,7 +226,6 @@ export class SessionIndex {
232
226
  }
233
227
  // Fingerprint changed → re-parse this single file
234
228
  const parsed = parseSessionJsonl(p);
235
- this.parseCount++;
236
229
  try {
237
230
  const st = fs.statSync(p, { bigint: true });
238
231
  const updated = {
@@ -303,7 +296,6 @@ export class SessionIndex {
303
296
  // Concurrent-write guard: stat before and after parse, compare fingerprints
304
297
  const fpBefore = fp;
305
298
  const parsed = parseSessionJsonl(fullPath);
306
- this.parseCount++;
307
299
  const fpAfter = buildFingerprint(fullPath);
308
300
  if (fpBefore !== fpAfter) {
309
301
  // File was modified during parse → skip this round, retry next refresh
@@ -172,9 +172,9 @@ export class DshSessionReader {
172
172
  .filter((i) => i.cwd !== undefined && samePath(i.cwd, cwd))
173
173
  .map((i) => ({
174
174
  sessionId: String(i.sessionId),
175
- summary: '',
176
175
  mtime: i.updatedAt,
177
176
  ...sessionPreset(i),
177
+ ...sessionPreview(i),
178
178
  }));
179
179
  sortByRecencyDesc(filtered, (i) => i.mtime, (i) => i.sessionId);
180
180
  const { items: page, total } = paginate(filtered, opts ?? {});
@@ -200,6 +200,8 @@ export class DshSessionReader {
200
200
  return { events: [] };
201
201
  }
202
202
  const throughSeq = this.resolveThroughSeq(sessionId, session);
203
+ // 服务端投影带来的标题与末轮输入:详情卡头部用它们,不必再从事件里猜。
204
+ const preview = sessionPreview(session);
203
205
  const value = this.unary('session/page', {
204
206
  address: { kind: 'session', sessionId },
205
207
  throughSeq,
@@ -307,12 +309,16 @@ export class DshSessionReader {
307
309
  // 中断/仍在执行的调用没有结果:把它当独立事件留下,别让这一条消失。
308
310
  if (pendingTool)
309
311
  events.push(pendingTool);
312
+ // 头部信息口径:最近输入优先用服务端末轮 prompt(事件里可能只是当前 turn
313
+ // 的一部分),标题用服务端 title;两者都缺时保持原行为。
314
+ const headerLastInput = preview.lastUserMessage ?? displayTitle;
310
315
  return {
311
316
  // 展示仍按 maxEvents 尾截断(默认 50,保持原行为),usage 已在上面基于
312
317
  // 完整 history 累加。
313
318
  events: capEvents(events, opts?.maxEvents ?? 50),
314
319
  ...(sessionUsage ? { usage: sessionUsage } : {}),
315
- ...(displayTitle ? { displayTitle } : {}),
320
+ ...(headerLastInput ? { displayTitle: headerLastInput } : {}),
321
+ ...(preview.title ? { aiTitle: preview.title } : {}),
316
322
  };
317
323
  }
318
324
  catch (error) {
@@ -353,6 +359,55 @@ function sessionPreset(item) {
353
359
  const preset = item.projections?.values?.agentPreset ?? item.agentPreset;
354
360
  return preset ? { agentPreset: preset } : {};
355
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
+ }
356
411
  function emptyUsageTotals() {
357
412
  return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
358
413
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lark-remote",
3
- "version": "0.5.7",
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",