lark-remote 0.5.13 → 0.5.14

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 (126) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/agent-kinds.d.ts +27 -0
  3. package/dist/agent-kinds.js +38 -0
  4. package/dist/bridge/approval-actions.d.ts +95 -0
  5. package/dist/bridge/approval-actions.js +104 -0
  6. package/dist/bridge/final-usage.d.ts +8 -17
  7. package/dist/bridge/final-usage.js +1 -18
  8. package/dist/bridge/index.d.ts +46 -39
  9. package/dist/bridge/index.js +201 -229
  10. package/dist/bridge/queue-manager.d.ts +25 -1
  11. package/dist/bridge/queue-manager.js +179 -160
  12. package/dist/bridge/run-approval.d.ts +2 -1
  13. package/dist/bridge/run-usage.d.ts +16 -30
  14. package/dist/bridge/run-usage.js +41 -50
  15. package/dist/bridge/session-writeback.d.ts +3 -2
  16. package/dist/card/approval-render.d.ts +3 -2
  17. package/dist/card/approval-render.js +3 -2
  18. package/dist/{router → card}/card-helpers.d.ts +7 -7
  19. package/dist/{router → card}/card-helpers.js +8 -8
  20. package/dist/card/card-session.d.ts +4 -2
  21. package/dist/card/card-session.js +8 -6
  22. package/dist/card/card-shared.d.ts +1 -1
  23. package/dist/card/card-shared.js +1 -1
  24. package/dist/card/final-answer-overflow.d.ts +0 -6
  25. package/dist/card/final-answer-overflow.js +1 -1
  26. package/dist/card/run-card-session.d.ts +2 -2
  27. package/dist/card/run-card-session.js +2 -2
  28. package/dist/card/run-renderer.js +7 -22
  29. package/dist/card/run-state.d.ts +28 -50
  30. package/dist/card/run-state.js +23 -0
  31. package/dist/card/usage-format.d.ts +30 -0
  32. package/dist/{router → card}/usage-format.js +3 -10
  33. package/dist/clone.js +6 -1
  34. package/dist/config/index.d.ts +16 -20
  35. package/dist/config/index.js +0 -16
  36. package/dist/config/pi-config.js +6 -4
  37. package/dist/connector/index.d.ts +2 -4
  38. package/dist/connector/index.js +7 -11
  39. package/dist/{runner/dsh → dsh}/auth.js +1 -1
  40. package/dist/dsh/protocol.d.ts +9 -0
  41. package/dist/dsh/protocol.js +61 -0
  42. package/dist/index.js +10 -5
  43. package/dist/model-catalog.d.ts +14 -0
  44. package/dist/model-catalog.js +25 -0
  45. package/dist/platform/path.d.ts +9 -0
  46. package/dist/platform/path.js +18 -0
  47. package/dist/router/active-run-usage.d.ts +4 -0
  48. package/dist/router/active-run-usage.js +12 -0
  49. package/dist/router/commands/ls.js +1 -1
  50. package/dist/router/commands/order.js +1 -1
  51. package/dist/router/commands/resume.d.ts +5 -4
  52. package/dist/router/commands/resume.js +4 -4
  53. package/dist/router/commands/ws.js +1 -1
  54. package/dist/router/config/claude.js +2 -1
  55. package/dist/router/config/common/render.d.ts +5 -1
  56. package/dist/router/config/common/render.js +6 -13
  57. package/dist/router/config/dsh.d.ts +1 -1
  58. package/dist/router/config/index.d.ts +1 -1
  59. package/dist/router/config/types.d.ts +11 -3
  60. package/dist/router/index.d.ts +41 -11
  61. package/dist/router/index.js +165 -226
  62. package/dist/runner/agent-choices-common.d.ts +1 -1
  63. package/dist/runner/claude/runner.d.ts +20 -2
  64. package/dist/runner/claude/runner.js +2 -15
  65. package/dist/runner/codex/app-server/runner.d.ts +3 -1
  66. package/dist/runner/codex/app-server/translator.d.ts +11 -0
  67. package/dist/runner/codex/app-server/translator.js +56 -149
  68. package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
  69. package/dist/runner/common/connection-based-runner.d.ts +3 -1
  70. package/dist/runner/common/jsonrpc/transport.js +17 -7
  71. package/dist/runner/dsh/client.d.ts +1 -4
  72. package/dist/runner/dsh/client.js +2 -55
  73. package/dist/runner/dsh/daemon.js +9 -2
  74. package/dist/runner/dsh/exit-cancel.js +1 -1
  75. package/dist/runner/dsh/mux-stream.js +21 -2
  76. package/dist/runner/dsh/runner.d.ts +21 -1
  77. package/dist/runner/dsh/runner.js +76 -73
  78. package/dist/runner/dsh/translator.d.ts +1 -1
  79. package/dist/runner/index.d.ts +1 -1
  80. package/dist/runner/kimi/acp/runner.d.ts +22 -1
  81. package/dist/runner/kimi/acp/runner.js +83 -60
  82. package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
  83. package/dist/runner/opencode/acp/error-monitor.js +31 -2
  84. package/dist/runner/opencode/acp/runner.d.ts +3 -1
  85. package/dist/runner/pi/rpc/runner.d.ts +2 -1
  86. package/dist/runner/probe.d.ts +1 -1
  87. package/dist/runner/probe.js +10 -9
  88. package/dist/runner/registry.d.ts +2 -1
  89. package/dist/runner/types.d.ts +20 -154
  90. package/dist/runner/types.js +4 -24
  91. package/dist/session/claude/session-index.d.ts +11 -4
  92. package/dist/session/claude/session-index.js +30 -8
  93. package/dist/session/claude/session-reader.d.ts +1 -1
  94. package/dist/session/claude/sessions.d.ts +2 -2
  95. package/dist/session/claude/sessions.js +41 -26
  96. package/dist/session/codex/rollout-reader.d.ts +1 -1
  97. package/dist/session/codex/rollout-reader.js +14 -13
  98. package/dist/session/codex/sessions.d.ts +1 -1
  99. package/dist/session/codex/sessions.js +4 -2
  100. package/dist/session/common/jsonl.js +7 -1
  101. package/dist/session/common/pagination.d.ts +2 -0
  102. package/dist/session/common/pagination.js +1 -1
  103. package/dist/session/common/recency.d.ts +6 -0
  104. package/dist/session/common/recency.js +7 -5
  105. package/dist/session/common/two-pass.d.ts +1 -1
  106. package/dist/session/common/usage-accumulator.d.ts +1 -1
  107. package/dist/session/dsh/sessions.d.ts +1 -1
  108. package/dist/session/dsh/sessions.js +37 -12
  109. package/dist/session/kimi/sessions.d.ts +1 -1
  110. package/dist/session/kimi/sessions.js +35 -8
  111. package/dist/session/opencode/sessions.d.ts +1 -1
  112. package/dist/session/opencode/sessions.js +51 -47
  113. package/dist/session/pi/sessions.d.ts +1 -1
  114. package/dist/session/pi/sessions.js +20 -7
  115. package/dist/session/registry.d.ts +2 -1
  116. package/dist/session/session-store.d.ts +1 -2
  117. package/dist/session/session-store.js +14 -7
  118. package/dist/session-contract.d.ts +178 -0
  119. package/dist/session-contract.js +9 -0
  120. package/package.json +1 -1
  121. package/dist/router/usage-format.d.ts +0 -60
  122. /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
  123. /package/dist/{runner/dsh → dsh}/home.d.ts +0 -0
  124. /package/dist/{runner/dsh → dsh}/home.js +0 -0
  125. /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
  126. /package/dist/{runner/dsh → dsh}/types.js +0 -0
@@ -1,18 +1,5 @@
1
- /**
2
- * Claude Runner 实现
3
- *
4
- * ClaudeRunner 从「一次一跑」(claude -p <msg> --permission-mode
5
- * bypassPermissions)升级为长驻交互会话(--input-format stream-json 双向
6
- * 通道),同时复用本项目已有的 ApprovalCoordinator + run 卡审批区。
7
- *
8
- * 架构:ClaudeRunner 是 workspace-lifetime 的薄包装,进程与协议细节在
9
- * ClaudeSession(src/runner/claude/session.ts,extends SpawningRunner 复用
10
- * pid 文件/killOrphan/Terminator/心跳/退出分发器机制)。每次 run() 委托
11
- * session:确保进程(可 --resume)→ 写 user 消息 → 消费事件直到本 turn result。
12
- */
13
- import { ClaudeSessionReader } from '../../session/claude/index.js';
14
1
  import { ClaudeSession } from './session.js';
15
- import { MODEL_ID_TO_ALIAS } from '../../config/index.js';
2
+ import { MODEL_ID_TO_ALIAS } from '../../model-catalog.js';
16
3
  /**
17
4
  * Claude 内建压缩命令。stream-json 输入模式下该文本会被 CLI 本地拦截为
18
5
  * slash 命令(不会作为模型提示词转发),压缩 turn 以普通 result 收尾
@@ -40,7 +27,7 @@ export class ClaudeRunner {
40
27
  this.defaultModel = opts.model ?? 'claude-opus-4-8';
41
28
  this.defaultEffort = opts.effort ?? 'medium';
42
29
  this.permissionMode = opts.permissionMode ?? 'auto';
43
- this.sessionReader = opts.sessionReader ?? new ClaudeSessionReader();
30
+ this.sessionReader = opts.sessionReader;
44
31
  this.session = new ClaudeSession({
45
32
  pidDir: opts.pidDir,
46
33
  cwd: opts.cwd,
@@ -10,7 +10,9 @@
10
10
  * queue + waitResolve pump, forceFinish/stopRequested semantics, turn idle
11
11
  * watchdog, and ConnectionLostError respawn-and-retry-once.
12
12
  */
13
- import type { AgentKind, AgentSessionReader, AgentEvent, AgentStatusInfo, SpawnOptions } from '../../types.js';
13
+ import type { AgentKind } from '../../../agent-kinds.js';
14
+ import type { AgentSessionReader } from '../../../session-contract.js';
15
+ import type { AgentEvent, AgentStatusInfo, SpawnOptions } from '../../types.js';
14
16
  import { JsonRpcClient } from '../../common/jsonrpc/client.js';
15
17
  import { type TranslatorEvent } from './translator.js';
16
18
  import { type AskForApproval, type SandboxMode, type InitializeResult } from './protocol-types.js';
@@ -105,6 +105,17 @@ export declare class CodexAppServerTranslator {
105
105
  private handleCommandApproval;
106
106
  private handleFileApproval;
107
107
  private handleItemStarted;
108
+ /**
109
+ * turn_diff 的统一信封:`type` + threadId/turnId/timestamp 三个公共字段在此
110
+ * 一次装配(十余个构造点原本各抄一遍,还得挂 `as TurnDiffEvent`)。
111
+ */
112
+ private turnDiffEvent;
113
+ /**
114
+ * 工具型 item 的 started 处理(commandExecution / webSearch / mcpToolCall /
115
+ * dynamicToolCall 四个分支同形):登记「已开始」锚点 + 记录工具身份 meta,
116
+ * 再发一帧空的 turn_diff 把工具块按真实时序插进卡片。
117
+ */
118
+ private beginToolItem;
108
119
  private handlePermissionsApproval;
109
120
  private buildPermissionItems;
110
121
  /** Get (or lazily create) the reasoning accumulator for one item. */
@@ -152,15 +152,10 @@ export class CodexAppServerTranslator {
152
152
  complete: true,
153
153
  timestamp: this.now(),
154
154
  }
155
- : {
156
- type: 'turn_diff',
157
- text,
158
- itemId: item.id,
159
- complete: true,
155
+ : this.turnDiffEvent({
160
156
  threadId: params?.threadId ?? this.threadId,
161
157
  turnId: params?.turn?.id ?? this.turnId,
162
- timestamp: this.now(),
163
- });
158
+ }, { text, itemId: item.id, complete: true }));
164
159
  }
165
160
  }
166
161
  else if (item.type === 'plan') {
@@ -169,15 +164,10 @@ export class CodexAppServerTranslator {
169
164
  const planText = item.text ?? '';
170
165
  if (planText && this.planByItem.get(item.id) !== planText) {
171
166
  this.planByItem.set(item.id, planText);
172
- events.push({
173
- type: 'turn_diff',
174
- plan: planText,
175
- itemId: item.id,
176
- complete: true,
167
+ events.push(this.turnDiffEvent({
177
168
  threadId: params?.threadId ?? this.threadId,
178
169
  turnId: params?.turn?.id ?? this.turnId,
179
- timestamp: this.now(),
180
- });
170
+ }, { plan: planText, itemId: item.id, complete: true }));
181
171
  }
182
172
  }
183
173
  }
@@ -272,30 +262,17 @@ export class CodexAppServerTranslator {
272
262
  },
273
263
  ];
274
264
  }
275
- return [
276
- {
277
- type: 'turn_diff',
278
- text: next,
279
- itemId: params.itemId,
280
- threadId: params.threadId,
281
- turnId: params.turnId,
282
- timestamp: this.now(),
283
- },
284
- ];
265
+ return [this.turnDiffEvent(params, { text: next, itemId: params.itemId })];
285
266
  }
286
267
  handleReasoningDelta(params) {
287
268
  // summaryTextDelta 流的是摘要文本;与 content 段落分开累积,避免段落交错。
288
269
  const entry = this.reasoningEntry(params.itemId);
289
270
  entry.summary += params.delta;
290
271
  return [
291
- {
292
- type: 'turn_diff',
272
+ this.turnDiffEvent(params, {
293
273
  reasoning: this.reasoningSnapshotFor(entry),
294
274
  itemId: params.itemId,
295
- threadId: params.threadId,
296
- turnId: params.turnId,
297
- timestamp: this.now(),
298
- },
275
+ }),
299
276
  ];
300
277
  }
301
278
  handleReasoningPartAdded() {
@@ -310,15 +287,11 @@ export class CodexAppServerTranslator {
310
287
  // 权威文件变更(含聚合后的 diff);以快照语义替换本 item 的变更列表。
311
288
  this.fileChangeItems.set(item.id, item.changes);
312
289
  return [
313
- {
314
- type: 'turn_diff',
290
+ this.turnDiffEvent(params, {
315
291
  fileChanges: normalizeFileChanges(item.changes),
316
292
  itemId: item.id,
317
293
  complete: true,
318
- threadId: params.threadId,
319
- turnId: params.turnId,
320
- timestamp: this.now(),
321
- },
294
+ }),
322
295
  ];
323
296
  }
324
297
  if (item.type === 'reasoning') {
@@ -330,15 +303,11 @@ export class CodexAppServerTranslator {
330
303
  entry.content = new Map(content.map((c, i) => [i, c]));
331
304
  entry.summary = summary.join('\n');
332
305
  return [
333
- {
334
- type: 'turn_diff',
306
+ this.turnDiffEvent(params, {
335
307
  reasoning: this.reasoningSnapshotFor(entry),
336
308
  itemId: item.id,
337
309
  complete: true,
338
- threadId: params.threadId,
339
- turnId: params.turnId,
340
- timestamp: this.now(),
341
- },
310
+ }),
342
311
  ];
343
312
  }
344
313
  }
@@ -358,17 +327,13 @@ export class CodexAppServerTranslator {
358
327
  const meta = this.toolItemMeta.get(item.id);
359
328
  this.toolItemMeta.delete(item.id);
360
329
  return [
361
- {
362
- type: 'turn_diff',
330
+ this.turnDiffEvent(params, {
363
331
  toolOutput: authoritative,
364
332
  ...meta,
365
333
  itemId: item.id,
366
334
  complete: true,
367
335
  toolStatus: commandExecutionStatusToToolStatus(item.status),
368
- threadId: params.threadId,
369
- turnId: params.turnId,
370
- timestamp: this.now(),
371
- },
336
+ }),
372
337
  ];
373
338
  }
374
339
  if (item.type === 'agentMessage') {
@@ -395,15 +360,11 @@ export class CodexAppServerTranslator {
395
360
  ];
396
361
  }
397
362
  return [
398
- {
399
- type: 'turn_diff',
363
+ this.turnDiffEvent(params, {
400
364
  text: finalText,
401
365
  itemId: item.id,
402
366
  complete: true,
403
- threadId: params.threadId,
404
- turnId: params.turnId,
405
- timestamp: this.now(),
406
- },
367
+ }),
407
368
  ];
408
369
  }
409
370
  }
@@ -412,15 +373,11 @@ export class CodexAppServerTranslator {
412
373
  if (text) {
413
374
  this.planByItem.set(item.id, text);
414
375
  return [
415
- {
416
- type: 'turn_diff',
376
+ this.turnDiffEvent(params, {
417
377
  plan: text,
418
378
  itemId: item.id,
419
379
  complete: true,
420
- threadId: params.threadId,
421
- turnId: params.turnId,
422
- timestamp: this.now(),
423
- },
380
+ }),
424
381
  ];
425
382
  }
426
383
  }
@@ -432,16 +389,12 @@ export class CodexAppServerTranslator {
432
389
  this.toolItemMeta.delete(item.id);
433
390
  this.startedToolItems.delete(item.id);
434
391
  return [
435
- {
436
- type: 'turn_diff',
392
+ this.turnDiffEvent(params, {
437
393
  toolOutput: this.toolOutputByItem.get(item.id) ?? '',
438
394
  ...meta,
439
395
  itemId: item.id,
440
396
  complete: true,
441
- threadId: params.threadId,
442
- turnId: params.turnId,
443
- timestamp: this.now(),
444
- },
397
+ }),
445
398
  ];
446
399
  }
447
400
  return [];
@@ -452,14 +405,10 @@ export class CodexAppServerTranslator {
452
405
  const prev = entry.content.get(index) ?? '';
453
406
  entry.content.set(index, prev + params.delta);
454
407
  return [
455
- {
456
- type: 'turn_diff',
408
+ this.turnDiffEvent(params, {
457
409
  reasoning: this.reasoningSnapshotFor(entry),
458
410
  itemId: params.itemId,
459
- threadId: params.threadId,
460
- turnId: params.turnId,
461
- timestamp: this.now(),
462
- },
411
+ }),
463
412
  ];
464
413
  }
465
414
  handleCommandOutputDelta(params) {
@@ -467,15 +416,11 @@ export class CodexAppServerTranslator {
467
416
  const next = prev + params.delta;
468
417
  this.toolOutputByItem.set(params.itemId, next);
469
418
  return [
470
- {
471
- type: 'turn_diff',
419
+ this.turnDiffEvent(params, {
472
420
  toolOutput: next,
473
421
  ...this.toolItemMeta.get(params.itemId),
474
422
  itemId: params.itemId,
475
- threadId: params.threadId,
476
- turnId: params.turnId,
477
- timestamp: this.now(),
478
- },
423
+ }),
479
424
  ];
480
425
  }
481
426
  handlePlanUpdated(params) {
@@ -485,14 +430,10 @@ export class CodexAppServerTranslator {
485
430
  const next = prev + params.delta;
486
431
  this.planByItem.set(params.itemId, next);
487
432
  return [
488
- {
489
- type: 'turn_diff',
433
+ this.turnDiffEvent(params, {
490
434
  plan: next,
491
435
  itemId: params.itemId,
492
- threadId: params.threadId,
493
- turnId: params.turnId,
494
- timestamp: this.now(),
495
- },
436
+ }),
496
437
  ];
497
438
  }
498
439
  handleTokenUsageUpdated(params) {
@@ -705,112 +646,78 @@ export class CodexAppServerTranslator {
705
646
  view: { ...pending.view, fileChanges },
706
647
  timestamp: new Date().toISOString(),
707
648
  },
708
- {
709
- type: 'turn_diff',
649
+ this.turnDiffEvent(params, {
710
650
  fileChanges,
711
651
  itemId: item.id,
712
- threadId: params.threadId,
713
- turnId: params.turnId,
714
- timestamp: this.now(),
715
- },
652
+ }),
716
653
  ];
717
654
  }
718
655
  }
719
656
  return [
720
- {
721
- type: 'turn_diff',
657
+ this.turnDiffEvent(params, {
722
658
  fileChanges: normalizeFileChanges(item.changes),
723
659
  itemId: item.id,
724
- threadId: params.threadId,
725
- turnId: params.turnId,
726
- timestamp: this.now(),
727
- },
660
+ }),
728
661
  ];
729
662
  }
730
663
  if (item.type === 'commandExecution') {
731
664
  // item/started 锚定工具块位置(真实时序):命令开始时即创建工具块,
732
665
  // 而不是等到首个输出 delta——否则后续 reasoning item 会插到 command 之前。
733
- this.startedToolItems.add(item.id);
734
- // 信息保真 记录工具身份,随 turn_diff 透传(run-state 首见时写入)。
735
- this.toolItemMeta.set(item.id, {
666
+ return this.beginToolItem(item.id, params, {
736
667
  toolName: 'Bash',
737
668
  toolInput: { command: item.command, ...(item.cwd ? { cwd: item.cwd } : {}) },
738
669
  toolHint: 'shell',
739
670
  });
740
- return [
741
- {
742
- type: 'turn_diff',
743
- toolOutput: '',
744
- ...this.toolItemMeta.get(item.id),
745
- itemId: item.id,
746
- threadId: params.threadId,
747
- turnId: params.turnId,
748
- timestamp: this.now(),
749
- },
750
- ];
751
671
  }
752
672
  if (item.type === 'webSearch') {
753
- this.startedToolItems.add(item.id);
754
- this.toolItemMeta.set(item.id, {
673
+ return this.beginToolItem(item.id, params, {
755
674
  toolName: 'WebSearch',
756
675
  toolInput: { query: item.query },
757
676
  toolHint: 'web',
758
677
  });
759
- return [
760
- {
761
- type: 'turn_diff',
762
- toolOutput: '',
763
- ...this.toolItemMeta.get(item.id),
764
- itemId: item.id,
765
- threadId: params.threadId,
766
- turnId: params.turnId,
767
- timestamp: this.now(),
768
- },
769
- ];
770
678
  }
771
679
  if (item.type === 'mcpToolCall') {
772
680
  // ThreadItem union 含 catch-all 成员,判别收窄会与泛型成员并集;
773
681
  // 显式断言到具体 interface(字段名已经 v2 schema 确认)。
774
682
  const mcpItem = item;
775
- this.startedToolItems.add(item.id);
776
- this.toolItemMeta.set(item.id, {
683
+ return this.beginToolItem(item.id, params, {
777
684
  toolName: `mcp__${mcpItem.server}__${mcpItem.tool}`,
778
685
  toolInput: mcpItem.arguments ?? {},
779
686
  toolHint: 'search',
780
687
  });
781
- return [
782
- {
783
- type: 'turn_diff',
784
- toolOutput: '',
785
- ...this.toolItemMeta.get(item.id),
786
- itemId: item.id,
787
- threadId: params.threadId,
788
- turnId: params.turnId,
789
- timestamp: this.now(),
790
- },
791
- ];
792
688
  }
793
689
  if (item.type === 'dynamicToolCall') {
794
690
  const dynItem = item;
795
- this.startedToolItems.add(item.id);
796
- this.toolItemMeta.set(item.id, {
691
+ return this.beginToolItem(item.id, params, {
797
692
  toolName: dynItem.tool,
798
693
  toolInput: dynItem.arguments ?? {},
799
694
  });
800
- return [
801
- {
802
- type: 'turn_diff',
803
- toolOutput: '',
804
- ...this.toolItemMeta.get(item.id),
805
- itemId: item.id,
806
- threadId: params.threadId,
807
- turnId: params.turnId,
808
- timestamp: this.now(),
809
- },
810
- ];
811
695
  }
812
696
  return [];
813
697
  }
698
+ /**
699
+ * turn_diff 的统一信封:`type` + threadId/turnId/timestamp 三个公共字段在此
700
+ * 一次装配(十余个构造点原本各抄一遍,还得挂 `as TurnDiffEvent`)。
701
+ */
702
+ turnDiffEvent(ids, fields) {
703
+ return {
704
+ type: 'turn_diff',
705
+ ...fields,
706
+ threadId: ids.threadId,
707
+ turnId: ids.turnId,
708
+ timestamp: this.now(),
709
+ };
710
+ }
711
+ /**
712
+ * 工具型 item 的 started 处理(commandExecution / webSearch / mcpToolCall /
713
+ * dynamicToolCall 四个分支同形):登记「已开始」锚点 + 记录工具身份 meta,
714
+ * 再发一帧空的 turn_diff 把工具块按真实时序插进卡片。
715
+ */
716
+ beginToolItem(itemId, params, meta) {
717
+ this.startedToolItems.add(itemId);
718
+ this.toolItemMeta.set(itemId, meta);
719
+ return [this.turnDiffEvent(params, { toolOutput: '', ...meta, itemId })];
720
+ }
814
721
  handlePermissionsApproval(requestId, params) {
815
722
  const permItems = this.buildPermissionItems(params.permissions);
816
723
  const view = {
@@ -18,7 +18,9 @@
18
18
  * Bash notifications and serves terminal/* reverse RPC; opencode is plain
19
19
  * - getStatusInfo / updateApprovalMode: agent-specific extras & mode vocab
20
20
  */
21
- import type { AgentEvent, AgentKind, AgentSessionReader, SpawnOptions } from '../../types.js';
21
+ import type { AgentKind } from '../../../agent-kinds.js';
22
+ import type { AgentSessionReader } from '../../../session-contract.js';
23
+ import type { AgentEvent, SpawnOptions } from '../../types.js';
22
24
  import { ConnectionManager } from '../jsonrpc/connection-manager.js';
23
25
  import { JsonRpcClient } from '../jsonrpc/client.js';
24
26
  import { type SessionNewResult, type SessionResumeResult, type SessionPromptResult, type RequestPermissionParams } from './protocol-types.js';
@@ -14,7 +14,9 @@
14
14
  * handleNotification/handleServerRequest, respondApproval, pendingApprovals,
15
15
  * updateApprovalMode, getStatusInfo) and provide the small abstract hooks below.
16
16
  */
17
- import type { AgentKind, AgentSessionReader, AgentEvent, AgentRunner, AgentStatusInfo, SpawnOptions } from '../types.js';
17
+ import type { AgentKind } from '../../agent-kinds.js';
18
+ import type { AgentSessionReader } from '../../session-contract.js';
19
+ import type { AgentEvent, AgentRunner, AgentStatusInfo, SpawnOptions } from '../types.js';
18
20
  import type { AgentStopper } from '../../platform/agent-stopper.js';
19
21
  export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent> implements AgentRunner {
20
22
  readonly kind: AgentKind;
@@ -12,6 +12,10 @@ import { binaryName } from '../../../platform/identity.js';
12
12
  import { createTerminator } from '../../../platform/terminator.js';
13
13
  import { agentStopperRegistry, } from '../../../platform/agent-stopper.js';
14
14
  import { getLogger } from '../../../logger/index.js';
15
+ /** 日志用的错误摘要(onClose 的 reason 仍是稳定分类码)。 */
16
+ function describeErr(err) {
17
+ return err instanceof Error ? err.message : String(err);
18
+ }
15
19
  /** Max bytes for a single line before we disconnect (10MB). */
16
20
  const MAX_LINE_BYTES = 10 * 1024 * 1024;
17
21
  /** Max bytes retained from stderr for diagnostics (64KB). */
@@ -205,8 +209,8 @@ export class JsonlRpcTransport {
205
209
  this.writeQueue.push(Buffer.from(JSON.stringify(msg) + '\n', 'utf8'));
206
210
  this.flush();
207
211
  }
208
- catch {
209
- this.handleClose('epipe');
212
+ catch (err) {
213
+ this.handleClose('epipe', describeErr(err));
210
214
  }
211
215
  }
212
216
  /** Send queued lines to stdin, pausing on backpressure until drain. */
@@ -229,7 +233,7 @@ export class JsonlRpcTransport {
229
233
  try {
230
234
  const ok = stdin.write(chunk, (err) => {
231
235
  if (err) {
232
- this.handleClose('epipe');
236
+ this.handleClose('epipe', describeErr(err));
233
237
  }
234
238
  });
235
239
  if (!ok) {
@@ -240,8 +244,8 @@ export class JsonlRpcTransport {
240
244
  next();
241
245
  }
242
246
  }
243
- catch {
244
- this.handleClose('epipe');
247
+ catch (err) {
248
+ this.handleClose('epipe', describeErr(err));
245
249
  }
246
250
  };
247
251
  next();
@@ -288,10 +292,11 @@ export class JsonlRpcTransport {
288
292
  this.registration = null;
289
293
  this.stoppers.unregister(this.agent, reg.pid, reg.stopper);
290
294
  }
291
- handleClose(reason) {
295
+ handleClose(reason, detail) {
292
296
  if (this._closed)
293
297
  return;
294
298
  this._closed = true;
299
+ const dropped = this.writeQueue.length;
295
300
  this.writeQueue = [];
296
301
  // Remove the exit listener to avoid double-firing
297
302
  if (this.proc) {
@@ -319,7 +324,12 @@ export class JsonlRpcTransport {
319
324
  // 统一关闭出口:EPIPE/超长行/进程 error 都必须通知上层(connection-manager
320
325
  // 靠 onClose 删除 slot、client 靠它 failPending),否则 slot 悬挂死连接。
321
326
  this.events?.onClose(reason);
322
- getLogger().info(`[jsonrpc-transport] closed: ${reason}`);
327
+ // reason 是给上层的稳定分类码;原始 err 与被丢弃的排队条数只进日志,
328
+ // 否则「stdin 写失败」到底写了什么、丢了什么都查不出来。
329
+ const suffix = [detail, dropped > 0 ? `${dropped} queued message(s) dropped` : undefined]
330
+ .filter(Boolean)
331
+ .join('; ');
332
+ getLogger().info(`[jsonrpc-transport] closed: ${reason}${suffix ? ` (${suffix})` : ''}`);
323
333
  }
324
334
  }
325
335
  //# sourceMappingURL=transport.js.map
@@ -19,7 +19,7 @@
19
19
  * `sessionEvents()` adds per-session filtering, reconnect + follow-snapshot
20
20
  * replay, and seq-based dedup.
21
21
  */
22
- import type { DshApprovalStreamItem, DshCommandResult, DshModelCatalogValue, DshModelSelection, DshOpenTurnState, DshPermissionCatalogValue, DshJobView, DshPresetListValue, DshProjectionItem, DshProjectionSnapshot, DshQuestion, DshRemoteEventOutcome, DshSessionPageValue, DshStreamItem, DshTokenUsage } from './types.js';
22
+ import type { DshApprovalStreamItem, DshCommandResult, DshModelCatalogValue, DshModelSelection, DshOpenTurnState, DshPermissionCatalogValue, DshJobView, DshPresetListValue, DshProjectionItem, DshProjectionSnapshot, DshQuestion, DshRemoteEventOutcome, DshSessionPageValue, DshStreamItem, DshTokenUsage } from '../../dsh/types.js';
23
23
  import type { GoalView } from '../types.js';
24
24
  export { DshError } from './errors.js';
25
25
  interface WebSocketMessageEvent {
@@ -72,9 +72,6 @@ export interface DshClientOptions {
72
72
  */
73
73
  requestTimeoutMs?: number;
74
74
  }
75
- /** Extract the `token` query parameter from a pasted `dsh web` URL. */
76
- export declare function extractDshToken(raw: string | undefined): string;
77
- export declare function dshArgs(method: string, payload: unknown): Record<string, unknown>;
78
75
  export declare class DshClient {
79
76
  readonly baseUrl: string;
80
77
  private readonly token;
@@ -21,8 +21,9 @@
21
21
  */
22
22
  import { randomUUID } from 'node:crypto';
23
23
  import WebSocket from 'ws';
24
+ import { dshArgs, extractDshToken } from '../../dsh/protocol.js';
24
25
  import { getLogger } from '../../logger/index.js';
25
- import { DSH_COOKIE_LIFETIME_MS, isDshCredentialsNotFound, mintDshBrowserCookie, readDshBrowserSecret, resolveDshCredentialsFile, } from './auth.js';
26
+ import { DSH_COOKIE_LIFETIME_MS, isDshCredentialsNotFound, mintDshBrowserCookie, readDshBrowserSecret, resolveDshCredentialsFile, } from '../../dsh/auth.js';
26
27
  import { DEFAULT_REQUEST_TIMEOUT_MS } from '../common/defaults.js';
27
28
  import { goalFromProjection } from './goal-view.js';
28
29
  import { DshError } from './errors.js';
@@ -111,60 +112,6 @@ function inboxItemsOf(values) {
111
112
  }
112
113
  return out;
113
114
  }
114
- /** Extract the `token` query parameter from a pasted `dsh web` URL. */
115
- export function extractDshToken(raw) {
116
- const value = (raw ?? '').trim();
117
- if (!value)
118
- return '';
119
- if (!value.includes('token=') && !value.includes('://'))
120
- return value;
121
- try {
122
- const url = new URL(value.startsWith('?') ? `http://dsh.invalid/${value}` : value);
123
- const token = url.searchParams.get('token');
124
- if (token)
125
- return token;
126
- // 带 scheme 却没有 token 参数的输入是误贴的 host URL(如
127
- // `http://127.0.0.1:3080`):整串不是合法 token,参与握手只会得到
128
- // 指向性缺失的 401。返回空串让上游走「token 缺失」的诊断路径。
129
- if (value.includes('://'))
130
- return '';
131
- return value;
132
- }
133
- catch {
134
- return value;
135
- }
136
- }
137
- /**
138
- * Remote methods take named parameters. Most session methods call their single
139
- * argument `request`; `session/list` reserves `_request`; catalog methods take
140
- * no arguments. `commands/*` address the receiving agent by its wire field
141
- * `agentId`, so their payload is already in wire shape (pass-through).
142
- * The gateway rejects any other `args` shape.
143
- */
144
- const DSH_ARGS_KEY = {
145
- 'session/list': '_request',
146
- 'session/create': 'request',
147
- 'session/prompt': 'request',
148
- 'session/cancel': 'request',
149
- 'session/selectModel': 'request',
150
- 'session/projections': 'request',
151
- 'session/page': 'request',
152
- 'session/follow': 'request',
153
- 'session/updateQueue': 'request',
154
- 'job/list': 'request',
155
- 'job/kill': 'request',
156
- 'session/modelCatalog': null,
157
- 'agentPresets/list': null,
158
- 'permissionPresets/catalog': null,
159
- };
160
- export function dshArgs(method, payload) {
161
- const key = DSH_ARGS_KEY[method];
162
- if (key === null)
163
- return {};
164
- if (key === undefined)
165
- return payload;
166
- return { [key]: payload };
167
- }
168
115
  export class DshClient {
169
116
  baseUrl;
170
117
  token;
@@ -15,7 +15,8 @@ import fsp from 'node:fs/promises';
15
15
  import path from 'node:path';
16
16
  import { DEFAULT_DSH_HOST } from '../../config/index.js';
17
17
  import { mergeProcessEnv, spawnProcess } from '../../platform/spawn.js';
18
- import { resolveDshHome } from './home.js';
18
+ import { getLogger } from '../../logger/index.js';
19
+ import { resolveDshHome } from '../../dsh/home.js';
19
20
  const DEFAULT_START_TIMEOUT_MS = 30_000;
20
21
  const READY_POLL_MS = 100;
21
22
  /** User-facing daemon failure; the bridge renders `message` in the chat card. */
@@ -174,7 +175,13 @@ export class DshDaemon {
174
175
  const stats = await fsp.stat(lockDir);
175
176
  return Date.now() - stats.mtimeMs > this.startTimeoutMs() * 2;
176
177
  }
177
- catch {
178
+ catch (err) {
179
+ // ENOENT = 锁目录刚被持有者清掉(正常,等下一轮重查)。
180
+ // 其余(EACCES/EPERM…)= 无法判断是否陈旧:保守判「不陈旧」继续等,
181
+ // 但必须留痕,否则超时文案会误报成「等待其他 lark-remote」。
182
+ if (err.code !== 'ENOENT') {
183
+ getLogger().warn(`[dsh] cannot stat start lock ${lockDir}: ${err?.message ?? String(err)}`);
184
+ }
178
185
  return false;
179
186
  }
180
187
  }
@@ -18,7 +18,7 @@ import path from 'node:path';
18
18
  import { getLogger } from '../../logger/index.js';
19
19
  import { isWin32 } from '../../platform/select.js';
20
20
  import { spawnProcessSync } from '../../platform/spawn.js';
21
- import { DSH_COOKIE_LIFETIME_MS, mintDshBrowserCookie, readDshBrowserSecretSync, resolveDshCredentialsFile, } from './auth.js';
21
+ import { DSH_COOKIE_LIFETIME_MS, mintDshBrowserCookie, readDshBrowserSecretSync, resolveDshCredentialsFile, } from '../../dsh/auth.js';
22
22
  /** Upper bound on the exit-path cancel:退出不能被一个卡住的 host 拖住。 */
23
23
  const EXIT_CANCEL_TIMEOUT_MS = 2_000;
24
24
  /**