lark-remote 0.5.12 → 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 (172) hide show
  1. package/CHANGELOG.md +28 -2
  2. package/README.en.md +1 -14
  3. package/README.md +1 -14
  4. package/dist/agent-kinds.d.ts +27 -0
  5. package/dist/agent-kinds.js +38 -0
  6. package/dist/bridge/approval-actions.d.ts +95 -0
  7. package/dist/bridge/approval-actions.js +104 -0
  8. package/dist/bridge/approval-coordinator.d.ts +1 -0
  9. package/dist/bridge/approval-coordinator.js +12 -24
  10. package/dist/bridge/final-usage.d.ts +8 -17
  11. package/dist/bridge/final-usage.js +1 -18
  12. package/dist/bridge/index.d.ts +52 -38
  13. package/dist/bridge/index.js +255 -433
  14. package/dist/bridge/queue-manager.d.ts +25 -1
  15. package/dist/bridge/queue-manager.js +179 -160
  16. package/dist/bridge/run-approval.d.ts +16 -0
  17. package/dist/bridge/run-approval.js +41 -0
  18. package/dist/bridge/run-usage.d.ts +32 -0
  19. package/dist/bridge/run-usage.js +87 -0
  20. package/dist/bridge/session-writeback.d.ts +19 -0
  21. package/dist/bridge/session-writeback.js +36 -0
  22. package/dist/card/approval-render.d.ts +3 -2
  23. package/dist/card/approval-render.js +3 -2
  24. package/dist/card/bash-card-session.js +2 -5
  25. package/dist/{router → card}/card-helpers.d.ts +7 -7
  26. package/dist/{router → card}/card-helpers.js +8 -8
  27. package/dist/card/card-session.d.ts +6 -2
  28. package/dist/card/card-session.js +28 -28
  29. package/dist/card/card-shared.d.ts +4 -4
  30. package/dist/card/card-shared.js +4 -10
  31. package/dist/card/collapsible.js +3 -2
  32. package/dist/card/final-answer-overflow.d.ts +1 -0
  33. package/dist/card/final-answer-overflow.js +46 -0
  34. package/dist/card/run-card-session.d.ts +2 -2
  35. package/dist/card/run-card-session.js +4 -9
  36. package/dist/card/run-renderer.d.ts +14 -0
  37. package/dist/card/run-renderer.js +126 -23
  38. package/dist/card/run-state.d.ts +40 -51
  39. package/dist/card/run-state.js +96 -34
  40. package/dist/card/usage-format.d.ts +30 -0
  41. package/dist/{router → card}/usage-format.js +3 -10
  42. package/dist/clone.js +6 -1
  43. package/dist/config/codex-config.js +4 -4
  44. package/dist/config/index.d.ts +18 -20
  45. package/dist/config/index.js +6 -20
  46. package/dist/config/pi-config.js +6 -4
  47. package/dist/connector/index.d.ts +2 -4
  48. package/dist/connector/index.js +7 -11
  49. package/dist/{runner/dsh → dsh}/auth.js +3 -4
  50. package/dist/dsh/home.d.ts +2 -0
  51. package/dist/dsh/home.js +8 -0
  52. package/dist/dsh/protocol.d.ts +9 -0
  53. package/dist/dsh/protocol.js +61 -0
  54. package/dist/inbound/turn-assembler.js +6 -0
  55. package/dist/index.js +25 -414
  56. package/dist/message-handlers.d.ts +26 -0
  57. package/dist/message-handlers.js +275 -0
  58. package/dist/model-catalog.d.ts +14 -0
  59. package/dist/model-catalog.js +25 -0
  60. package/dist/platform/path.d.ts +9 -0
  61. package/dist/platform/path.js +18 -0
  62. package/dist/router/active-run-usage.d.ts +4 -0
  63. package/dist/router/active-run-usage.js +12 -0
  64. package/dist/router/card-action-payload.d.ts +1 -1
  65. package/dist/router/card-action-payload.js +1 -1
  66. package/dist/router/commands/ls.d.ts +9 -0
  67. package/dist/router/commands/ls.js +243 -0
  68. package/dist/router/commands/order.d.ts +12 -0
  69. package/dist/router/commands/order.js +178 -0
  70. package/dist/router/commands/resume.d.ts +48 -0
  71. package/dist/router/commands/resume.js +246 -0
  72. package/dist/router/commands/ws.d.ts +15 -0
  73. package/dist/router/commands/ws.js +189 -0
  74. package/dist/router/config/claude.js +5 -4
  75. package/dist/router/config/codex.js +2 -6
  76. package/dist/router/config/common/render.d.ts +5 -1
  77. package/dist/router/config/common/render.js +6 -13
  78. package/dist/router/config/common/turn-idle.d.ts +2 -0
  79. package/dist/router/config/common/turn-idle.js +9 -0
  80. package/dist/router/config/dsh.d.ts +1 -1
  81. package/dist/router/config/index.d.ts +1 -1
  82. package/dist/router/config/kimi.js +2 -6
  83. package/dist/router/config/opencode.js +2 -6
  84. package/dist/router/config/types.d.ts +11 -3
  85. package/dist/router/index.d.ts +55 -33
  86. package/dist/router/index.js +233 -1376
  87. package/dist/router/paging.d.ts +13 -0
  88. package/dist/router/paging.js +20 -0
  89. package/dist/router/path-validation.d.ts +27 -0
  90. package/dist/router/path-validation.js +45 -0
  91. package/dist/runner/agent-choices-common.d.ts +1 -1
  92. package/dist/runner/claude/runner.d.ts +23 -6
  93. package/dist/runner/claude/runner.js +6 -20
  94. package/dist/runner/claude/session.js +1 -1
  95. package/dist/runner/codex/app-server/runner.d.ts +3 -1
  96. package/dist/runner/codex/app-server/translator.d.ts +13 -0
  97. package/dist/runner/codex/app-server/translator.js +114 -151
  98. package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
  99. package/dist/runner/common/acp/base-translator.d.ts +3 -8
  100. package/dist/runner/common/acp/base-translator.js +16 -7
  101. package/dist/runner/common/connection-based-runner.d.ts +3 -1
  102. package/dist/runner/common/jsonrpc/transport.js +17 -7
  103. package/dist/runner/common/spawning-runner.js +10 -4
  104. package/dist/runner/common/turn-started.d.ts +2 -0
  105. package/dist/runner/common/turn-started.js +10 -0
  106. package/dist/runner/dsh/client.d.ts +2 -10
  107. package/dist/runner/dsh/client.js +13 -220
  108. package/dist/runner/dsh/daemon.d.ts +0 -2
  109. package/dist/runner/dsh/daemon.js +9 -7
  110. package/dist/runner/dsh/errors.d.ts +5 -0
  111. package/dist/runner/dsh/errors.js +11 -0
  112. package/dist/runner/dsh/exit-cancel.d.ts +0 -2
  113. package/dist/runner/dsh/exit-cancel.js +2 -2
  114. package/dist/runner/dsh/inflight.d.ts +0 -2
  115. package/dist/runner/dsh/inflight.js +1 -1
  116. package/dist/runner/dsh/mux-stream.d.ts +26 -0
  117. package/dist/runner/dsh/mux-stream.js +176 -0
  118. package/dist/runner/dsh/runner.d.ts +21 -1
  119. package/dist/runner/dsh/runner.js +76 -73
  120. package/dist/runner/dsh/translator.d.ts +1 -11
  121. package/dist/runner/dsh/translator.js +1 -1
  122. package/dist/runner/index.d.ts +1 -1
  123. package/dist/runner/kimi/acp/runner.d.ts +22 -1
  124. package/dist/runner/kimi/acp/runner.js +83 -60
  125. package/dist/runner/kimi/acp/translator.js +1 -12
  126. package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
  127. package/dist/runner/opencode/acp/error-monitor.js +31 -2
  128. package/dist/runner/opencode/acp/runner.d.ts +3 -1
  129. package/dist/runner/opencode/acp/translator.js +1 -12
  130. package/dist/runner/pi/rpc/runner.d.ts +2 -1
  131. package/dist/runner/pi/rpc/translator.d.ts +2 -8
  132. package/dist/runner/pi/rpc/translator.js +2 -7
  133. package/dist/runner/probe.d.ts +1 -1
  134. package/dist/runner/probe.js +10 -9
  135. package/dist/runner/registry.d.ts +2 -20
  136. package/dist/runner/registry.js +0 -32
  137. package/dist/runner/types.d.ts +37 -155
  138. package/dist/runner/types.js +4 -24
  139. package/dist/session/claude/session-index.d.ts +11 -4
  140. package/dist/session/claude/session-index.js +34 -10
  141. package/dist/session/claude/session-reader.d.ts +1 -1
  142. package/dist/session/claude/sessions.d.ts +2 -2
  143. package/dist/session/claude/sessions.js +41 -26
  144. package/dist/session/codex/rollout-reader.d.ts +1 -1
  145. package/dist/session/codex/rollout-reader.js +14 -13
  146. package/dist/session/codex/sessions.d.ts +1 -1
  147. package/dist/session/codex/sessions.js +4 -2
  148. package/dist/session/common/jsonl.js +7 -1
  149. package/dist/session/common/pagination.d.ts +2 -0
  150. package/dist/session/common/pagination.js +1 -1
  151. package/dist/session/common/recency.d.ts +6 -0
  152. package/dist/session/common/recency.js +7 -5
  153. package/dist/session/common/two-pass.d.ts +1 -1
  154. package/dist/session/common/usage-accumulator.d.ts +1 -1
  155. package/dist/session/dsh/sessions.d.ts +1 -1
  156. package/dist/session/dsh/sessions.js +37 -12
  157. package/dist/session/kimi/sessions.d.ts +1 -1
  158. package/dist/session/kimi/sessions.js +37 -10
  159. package/dist/session/opencode/sessions.d.ts +1 -1
  160. package/dist/session/opencode/sessions.js +53 -48
  161. package/dist/session/pi/sessions.d.ts +1 -1
  162. package/dist/session/pi/sessions.js +23 -9
  163. package/dist/session/registry.d.ts +2 -1
  164. package/dist/session/session-store.d.ts +1 -2
  165. package/dist/session/session-store.js +14 -7
  166. package/dist/session-contract.d.ts +178 -0
  167. package/dist/session-contract.js +9 -0
  168. package/package.json +1 -1
  169. package/dist/router/usage-format.d.ts +0 -60
  170. /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
  171. /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
  172. /package/dist/{runner/dsh → dsh}/types.js +0 -0
@@ -18,6 +18,8 @@ export class CodexAppServerTranslator {
18
18
  turnStatus = 'pending';
19
19
  /** Full agentMessage text per item id (item-scoped snapshot). */
20
20
  textByItem = new Map();
21
+ /** agentMessage item ids whose protocol phase is `final_answer`. */
22
+ finalTextItems = new Set();
21
23
  /**
22
24
  * Reasoning content per item id: paragraphs keyed by `contentIndex`
23
25
  * (textDelta) plus the summary stream (summaryTextDelta). Each reasoning
@@ -141,15 +143,19 @@ export class CodexAppServerTranslator {
141
143
  const text = item.text ?? '';
142
144
  if (text && this.textByItem.get(item.id) !== text) {
143
145
  this.textByItem.set(item.id, text);
144
- events.push({
145
- type: 'turn_diff',
146
- text,
147
- itemId: item.id,
148
- complete: true,
149
- threadId: params?.threadId ?? this.threadId,
150
- turnId: params?.turn?.id ?? this.turnId,
151
- timestamp: this.now(),
152
- });
146
+ const final = item.phase === 'final_answer';
147
+ events.push(final
148
+ ? {
149
+ type: 'final_answer',
150
+ text,
151
+ itemId: item.id,
152
+ complete: true,
153
+ timestamp: this.now(),
154
+ }
155
+ : this.turnDiffEvent({
156
+ threadId: params?.threadId ?? this.threadId,
157
+ turnId: params?.turn?.id ?? this.turnId,
158
+ }, { text, itemId: item.id, complete: true }));
153
159
  }
154
160
  }
155
161
  else if (item.type === 'plan') {
@@ -158,15 +164,10 @@ export class CodexAppServerTranslator {
158
164
  const planText = item.text ?? '';
159
165
  if (planText && this.planByItem.get(item.id) !== planText) {
160
166
  this.planByItem.set(item.id, planText);
161
- events.push({
162
- type: 'turn_diff',
163
- plan: planText,
164
- itemId: item.id,
165
- complete: true,
167
+ events.push(this.turnDiffEvent({
166
168
  threadId: params?.threadId ?? this.threadId,
167
169
  turnId: params?.turn?.id ?? this.turnId,
168
- timestamp: this.now(),
169
- });
170
+ }, { plan: planText, itemId: item.id, complete: true }));
170
171
  }
171
172
  }
172
173
  }
@@ -228,6 +229,7 @@ export class CodexAppServerTranslator {
228
229
  // 的 turnId 闸门挡掉。
229
230
  this.usage = { inputTokens: 0, outputTokens: 0 };
230
231
  this.textByItem.clear();
232
+ this.finalTextItems.clear();
231
233
  this.reasoningByItem.clear();
232
234
  this.toolOutputByItem.clear();
233
235
  this.toolItemMeta.clear();
@@ -249,30 +251,28 @@ export class CodexAppServerTranslator {
249
251
  const prev = this.textByItem.get(params.itemId) ?? '';
250
252
  const next = prev + params.delta;
251
253
  this.textByItem.set(params.itemId, next);
252
- return [
253
- {
254
- type: 'turn_diff',
255
- text: next,
256
- itemId: params.itemId,
257
- threadId: params.threadId,
258
- turnId: params.turnId,
259
- timestamp: this.now(),
260
- },
261
- ];
254
+ if (this.finalTextItems.has(params.itemId)) {
255
+ return [
256
+ {
257
+ type: 'final_answer',
258
+ text: next,
259
+ itemId: params.itemId,
260
+ complete: false,
261
+ timestamp: this.now(),
262
+ },
263
+ ];
264
+ }
265
+ return [this.turnDiffEvent(params, { text: next, itemId: params.itemId })];
262
266
  }
263
267
  handleReasoningDelta(params) {
264
268
  // summaryTextDelta 流的是摘要文本;与 content 段落分开累积,避免段落交错。
265
269
  const entry = this.reasoningEntry(params.itemId);
266
270
  entry.summary += params.delta;
267
271
  return [
268
- {
269
- type: 'turn_diff',
272
+ this.turnDiffEvent(params, {
270
273
  reasoning: this.reasoningSnapshotFor(entry),
271
274
  itemId: params.itemId,
272
- threadId: params.threadId,
273
- turnId: params.turnId,
274
- timestamp: this.now(),
275
- },
275
+ }),
276
276
  ];
277
277
  }
278
278
  handleReasoningPartAdded() {
@@ -287,15 +287,11 @@ export class CodexAppServerTranslator {
287
287
  // 权威文件变更(含聚合后的 diff);以快照语义替换本 item 的变更列表。
288
288
  this.fileChangeItems.set(item.id, item.changes);
289
289
  return [
290
- {
291
- type: 'turn_diff',
290
+ this.turnDiffEvent(params, {
292
291
  fileChanges: normalizeFileChanges(item.changes),
293
292
  itemId: item.id,
294
293
  complete: true,
295
- threadId: params.threadId,
296
- turnId: params.turnId,
297
- timestamp: this.now(),
298
- },
294
+ }),
299
295
  ];
300
296
  }
301
297
  if (item.type === 'reasoning') {
@@ -307,15 +303,11 @@ export class CodexAppServerTranslator {
307
303
  entry.content = new Map(content.map((c, i) => [i, c]));
308
304
  entry.summary = summary.join('\n');
309
305
  return [
310
- {
311
- type: 'turn_diff',
306
+ this.turnDiffEvent(params, {
312
307
  reasoning: this.reasoningSnapshotFor(entry),
313
308
  itemId: item.id,
314
309
  complete: true,
315
- threadId: params.threadId,
316
- turnId: params.turnId,
317
- timestamp: this.now(),
318
- },
310
+ }),
319
311
  ];
320
312
  }
321
313
  }
@@ -335,17 +327,13 @@ export class CodexAppServerTranslator {
335
327
  const meta = this.toolItemMeta.get(item.id);
336
328
  this.toolItemMeta.delete(item.id);
337
329
  return [
338
- {
339
- type: 'turn_diff',
330
+ this.turnDiffEvent(params, {
340
331
  toolOutput: authoritative,
341
332
  ...meta,
342
333
  itemId: item.id,
343
334
  complete: true,
344
335
  toolStatus: commandExecutionStatusToToolStatus(item.status),
345
- threadId: params.threadId,
346
- turnId: params.turnId,
347
- timestamp: this.now(),
348
- },
336
+ }),
349
337
  ];
350
338
  }
351
339
  if (item.type === 'agentMessage') {
@@ -358,16 +346,25 @@ export class CodexAppServerTranslator {
358
346
  if (text || accumulated) {
359
347
  const finalText = text || accumulated;
360
348
  this.textByItem.set(item.id, finalText);
349
+ if (item.phase === 'final_answer' ||
350
+ this.finalTextItems.has(item.id)) {
351
+ this.finalTextItems.delete(item.id);
352
+ return [
353
+ {
354
+ type: 'final_answer',
355
+ text: finalText,
356
+ itemId: item.id,
357
+ complete: true,
358
+ timestamp: this.now(),
359
+ },
360
+ ];
361
+ }
361
362
  return [
362
- {
363
- type: 'turn_diff',
363
+ this.turnDiffEvent(params, {
364
364
  text: finalText,
365
365
  itemId: item.id,
366
366
  complete: true,
367
- threadId: params.threadId,
368
- turnId: params.turnId,
369
- timestamp: this.now(),
370
- },
367
+ }),
371
368
  ];
372
369
  }
373
370
  }
@@ -376,15 +373,11 @@ export class CodexAppServerTranslator {
376
373
  if (text) {
377
374
  this.planByItem.set(item.id, text);
378
375
  return [
379
- {
380
- type: 'turn_diff',
376
+ this.turnDiffEvent(params, {
381
377
  plan: text,
382
378
  itemId: item.id,
383
379
  complete: true,
384
- threadId: params.threadId,
385
- turnId: params.turnId,
386
- timestamp: this.now(),
387
- },
380
+ }),
388
381
  ];
389
382
  }
390
383
  }
@@ -396,16 +389,12 @@ export class CodexAppServerTranslator {
396
389
  this.toolItemMeta.delete(item.id);
397
390
  this.startedToolItems.delete(item.id);
398
391
  return [
399
- {
400
- type: 'turn_diff',
392
+ this.turnDiffEvent(params, {
401
393
  toolOutput: this.toolOutputByItem.get(item.id) ?? '',
402
394
  ...meta,
403
395
  itemId: item.id,
404
396
  complete: true,
405
- threadId: params.threadId,
406
- turnId: params.turnId,
407
- timestamp: this.now(),
408
- },
397
+ }),
409
398
  ];
410
399
  }
411
400
  return [];
@@ -416,14 +405,10 @@ export class CodexAppServerTranslator {
416
405
  const prev = entry.content.get(index) ?? '';
417
406
  entry.content.set(index, prev + params.delta);
418
407
  return [
419
- {
420
- type: 'turn_diff',
408
+ this.turnDiffEvent(params, {
421
409
  reasoning: this.reasoningSnapshotFor(entry),
422
410
  itemId: params.itemId,
423
- threadId: params.threadId,
424
- turnId: params.turnId,
425
- timestamp: this.now(),
426
- },
411
+ }),
427
412
  ];
428
413
  }
429
414
  handleCommandOutputDelta(params) {
@@ -431,15 +416,11 @@ export class CodexAppServerTranslator {
431
416
  const next = prev + params.delta;
432
417
  this.toolOutputByItem.set(params.itemId, next);
433
418
  return [
434
- {
435
- type: 'turn_diff',
419
+ this.turnDiffEvent(params, {
436
420
  toolOutput: next,
437
421
  ...this.toolItemMeta.get(params.itemId),
438
422
  itemId: params.itemId,
439
- threadId: params.threadId,
440
- turnId: params.turnId,
441
- timestamp: this.now(),
442
- },
423
+ }),
443
424
  ];
444
425
  }
445
426
  handlePlanUpdated(params) {
@@ -449,14 +430,10 @@ export class CodexAppServerTranslator {
449
430
  const next = prev + params.delta;
450
431
  this.planByItem.set(params.itemId, next);
451
432
  return [
452
- {
453
- type: 'turn_diff',
433
+ this.turnDiffEvent(params, {
454
434
  plan: next,
455
435
  itemId: params.itemId,
456
- threadId: params.threadId,
457
- turnId: params.turnId,
458
- timestamp: this.now(),
459
- },
436
+ }),
460
437
  ];
461
438
  }
462
439
  handleTokenUsageUpdated(params) {
@@ -635,6 +612,26 @@ export class CodexAppServerTranslator {
635
612
  }
636
613
  handleItemStarted(params) {
637
614
  const item = params.item;
615
+ if (item.type === 'agentMessage') {
616
+ const phase = item.phase;
617
+ if (phase === 'final_answer') {
618
+ this.finalTextItems.add(item.id);
619
+ const text = item.text ?? '';
620
+ if (text) {
621
+ this.textByItem.set(item.id, text);
622
+ return [
623
+ {
624
+ type: 'final_answer',
625
+ text,
626
+ itemId: item.id,
627
+ complete: false,
628
+ timestamp: this.now(),
629
+ },
630
+ ];
631
+ }
632
+ }
633
+ return [];
634
+ }
638
635
  if (item.type === 'fileChange' && Array.isArray(item.changes)) {
639
636
  this.fileChangeItems.set(item.id, item.changes);
640
637
  const pending = this.pendingFileApprovalByItemId.get(item.id);
@@ -649,112 +646,78 @@ export class CodexAppServerTranslator {
649
646
  view: { ...pending.view, fileChanges },
650
647
  timestamp: new Date().toISOString(),
651
648
  },
652
- {
653
- type: 'turn_diff',
649
+ this.turnDiffEvent(params, {
654
650
  fileChanges,
655
651
  itemId: item.id,
656
- threadId: params.threadId,
657
- turnId: params.turnId,
658
- timestamp: this.now(),
659
- },
652
+ }),
660
653
  ];
661
654
  }
662
655
  }
663
656
  return [
664
- {
665
- type: 'turn_diff',
657
+ this.turnDiffEvent(params, {
666
658
  fileChanges: normalizeFileChanges(item.changes),
667
659
  itemId: item.id,
668
- threadId: params.threadId,
669
- turnId: params.turnId,
670
- timestamp: this.now(),
671
- },
660
+ }),
672
661
  ];
673
662
  }
674
663
  if (item.type === 'commandExecution') {
675
664
  // item/started 锚定工具块位置(真实时序):命令开始时即创建工具块,
676
665
  // 而不是等到首个输出 delta——否则后续 reasoning item 会插到 command 之前。
677
- this.startedToolItems.add(item.id);
678
- // 信息保真 记录工具身份,随 turn_diff 透传(run-state 首见时写入)。
679
- this.toolItemMeta.set(item.id, {
666
+ return this.beginToolItem(item.id, params, {
680
667
  toolName: 'Bash',
681
668
  toolInput: { command: item.command, ...(item.cwd ? { cwd: item.cwd } : {}) },
682
669
  toolHint: 'shell',
683
670
  });
684
- return [
685
- {
686
- type: 'turn_diff',
687
- toolOutput: '',
688
- ...this.toolItemMeta.get(item.id),
689
- itemId: item.id,
690
- threadId: params.threadId,
691
- turnId: params.turnId,
692
- timestamp: this.now(),
693
- },
694
- ];
695
671
  }
696
672
  if (item.type === 'webSearch') {
697
- this.startedToolItems.add(item.id);
698
- this.toolItemMeta.set(item.id, {
673
+ return this.beginToolItem(item.id, params, {
699
674
  toolName: 'WebSearch',
700
675
  toolInput: { query: item.query },
701
676
  toolHint: 'web',
702
677
  });
703
- return [
704
- {
705
- type: 'turn_diff',
706
- toolOutput: '',
707
- ...this.toolItemMeta.get(item.id),
708
- itemId: item.id,
709
- threadId: params.threadId,
710
- turnId: params.turnId,
711
- timestamp: this.now(),
712
- },
713
- ];
714
678
  }
715
679
  if (item.type === 'mcpToolCall') {
716
680
  // ThreadItem union 含 catch-all 成员,判别收窄会与泛型成员并集;
717
681
  // 显式断言到具体 interface(字段名已经 v2 schema 确认)。
718
682
  const mcpItem = item;
719
- this.startedToolItems.add(item.id);
720
- this.toolItemMeta.set(item.id, {
683
+ return this.beginToolItem(item.id, params, {
721
684
  toolName: `mcp__${mcpItem.server}__${mcpItem.tool}`,
722
685
  toolInput: mcpItem.arguments ?? {},
723
686
  toolHint: 'search',
724
687
  });
725
- return [
726
- {
727
- type: 'turn_diff',
728
- toolOutput: '',
729
- ...this.toolItemMeta.get(item.id),
730
- itemId: item.id,
731
- threadId: params.threadId,
732
- turnId: params.turnId,
733
- timestamp: this.now(),
734
- },
735
- ];
736
688
  }
737
689
  if (item.type === 'dynamicToolCall') {
738
690
  const dynItem = item;
739
- this.startedToolItems.add(item.id);
740
- this.toolItemMeta.set(item.id, {
691
+ return this.beginToolItem(item.id, params, {
741
692
  toolName: dynItem.tool,
742
693
  toolInput: dynItem.arguments ?? {},
743
694
  });
744
- return [
745
- {
746
- type: 'turn_diff',
747
- toolOutput: '',
748
- ...this.toolItemMeta.get(item.id),
749
- itemId: item.id,
750
- threadId: params.threadId,
751
- turnId: params.turnId,
752
- timestamp: this.now(),
753
- },
754
- ];
755
695
  }
756
696
  return [];
757
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
+ }
758
721
  handlePermissionsApproval(requestId, params) {
759
722
  const permItems = this.buildPermissionItems(params.permissions);
760
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';
@@ -19,16 +19,10 @@
19
19
  * buildToolUseUpdate — tool_use patch from tool_call_update
20
20
  * onUsageSample — extra usage bookkeeping (opencode: cost)
21
21
  */
22
- import type { AgentEvent, ApprovalRequestedEvent, ResultEvent } from '../../types.js';
22
+ import type { AgentEvent, ApprovalRequestedEvent, ApprovalView, ResultEvent, TurnStartedEvent } from '../../types.js';
23
23
  import { type AgentMessageChunkEvent, type AgentThoughtChunkEvent, type ToolCallEvent, type ToolCallUpdateEvent, type UsageUpdateEvent, type RequestPermissionParams, type AcpPlanEvent } from './protocol-types.js';
24
24
  /** Self-produced turn_started event (no wire equivalent — produced on prompt start). */
25
- export interface AcpTurnStartedEvent {
26
- type: 'turn_started';
27
- threadId: string;
28
- turnId: string;
29
- operationKind: 'turn' | 'compaction';
30
- timestamp?: string;
31
- }
25
+ export type AcpTurnStartedEvent = TurnStartedEvent;
32
26
  /**
33
27
  * Live usage snapshot from usage_update. The ACP wire carries context
34
28
  * occupancy (`used`) and the model context window (`size`) — there is NO
@@ -96,6 +90,7 @@ export declare abstract class BaseAcpTranslator {
96
90
  * is sent (no wire notification for this — self-produced).
97
91
  */
98
92
  produceTurnStarted(sessionId: string, turnId: string): AcpTurnStartedEvent;
93
+ protected approvalRequestedEvents(requestId: number | string, sessionId: string, view: ApprovalView): ApprovalRequestedEvent[];
99
94
  /**
100
95
  * Translate a prompt response into a result event.
101
96
  * stopReason: end_turn → success; cancelled → interrupted (independent
@@ -1,3 +1,4 @@
1
+ import { makeTurnStartedEvent } from '../turn-started.js';
1
2
  import { NotificationMethod, ServerRequestMethod, SessionEventType, } from './protocol-types.js';
2
3
  /**
3
4
  * ACP tool kind → 常用工具名映射(信息保真,数据驱动无 agent 分支)。
@@ -145,13 +146,21 @@ export class BaseAcpTranslator {
145
146
  */
146
147
  produceTurnStarted(sessionId, turnId) {
147
148
  this.currentTurnId = turnId;
148
- return {
149
- type: 'turn_started',
150
- threadId: sessionId,
151
- turnId,
152
- operationKind: this.operationKind === 'compact' ? 'compaction' : 'turn',
153
- timestamp: new Date().toISOString(),
154
- };
149
+ return makeTurnStartedEvent(sessionId, turnId, this.operationKind);
150
+ }
151
+ approvalRequestedEvents(requestId, sessionId, view) {
152
+ return [
153
+ {
154
+ type: 'approval_requested',
155
+ requestId,
156
+ kind: 'command',
157
+ threadId: sessionId,
158
+ turnId: '',
159
+ itemId: '',
160
+ view,
161
+ timestamp: new Date().toISOString(),
162
+ },
163
+ ];
155
164
  }
156
165
  /**
157
166
  * Translate a prompt response into a result event.
@@ -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
@@ -374,13 +374,19 @@ export class SpawningRunner {
374
374
  try {
375
375
  process.kill(-pid, 'SIGTERM');
376
376
  }
377
- catch {
378
- // process may have exited
377
+ catch (err) {
378
+ const code = err.code;
379
+ if (code === 'ESRCH') {
380
+ getLogger().debug(`[${this.logTag}] orphan process group ${-pid} already exited`);
381
+ }
382
+ else {
383
+ getLogger().warn(`[${this.logTag}] killOrphan failed to signal process group ${-pid}: ${err?.message ?? String(err)}`);
384
+ }
379
385
  }
380
386
  bestEffortUnlink(this.pidFilePath);
381
387
  }
382
- catch {
383
- // ignore
388
+ catch (err) {
389
+ getLogger().warn(`[${this.logTag}] killOrphan failed for ${this.pidFilePath}: ${err?.message ?? String(err)}`);
384
390
  }
385
391
  }
386
392
  /**
@@ -0,0 +1,2 @@
1
+ import type { TurnStartedEvent } from '../types.js';
2
+ export declare function makeTurnStartedEvent(sessionId: string, turnId: string, operationKind: 'turn' | 'compact'): TurnStartedEvent;
@@ -0,0 +1,10 @@
1
+ export function makeTurnStartedEvent(sessionId, turnId, operationKind) {
2
+ return {
3
+ type: 'turn_started',
4
+ threadId: sessionId,
5
+ turnId,
6
+ operationKind: operationKind === 'compact' ? 'compaction' : 'turn',
7
+ timestamp: new Date().toISOString(),
8
+ };
9
+ }
10
+ //# sourceMappingURL=turn-started.js.map