lark-remote 0.5.20 → 0.5.21

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 (55) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bridge/inbound-media.js +71 -45
  3. package/dist/bridge/index.d.ts +9 -0
  4. package/dist/bridge/index.js +230 -203
  5. package/dist/card/approval-render.js +32 -33
  6. package/dist/card/card-budget.js +123 -98
  7. package/dist/card/run-renderer.js +82 -68
  8. package/dist/card/run-state.d.ts +0 -14
  9. package/dist/card/run-state.js +335 -207
  10. package/dist/card/tool-render.js +20 -16
  11. package/dist/card/usage-format.d.ts +12 -9
  12. package/dist/card/usage-format.js +111 -97
  13. package/dist/common/text-width.js +36 -24
  14. package/dist/common/tool-summary.js +132 -90
  15. package/dist/index.js +226 -190
  16. package/dist/message-handlers.js +253 -236
  17. package/dist/router/commands/ls.d.ts +3 -0
  18. package/dist/router/commands/ls.js +256 -175
  19. package/dist/router/commands/order.js +184 -159
  20. package/dist/router/commands/resume.js +189 -133
  21. package/dist/router/commands/ws.js +219 -174
  22. package/dist/router/index.js +192 -201
  23. package/dist/runner/bash/runner.js +2 -4
  24. package/dist/runner/codex/app-server/translator.d.ts +2 -0
  25. package/dist/runner/codex/app-server/translator.js +287 -221
  26. package/dist/runner/common/acp/base-translator.js +19 -17
  27. package/dist/runner/common/jsonl-stream.js +154 -187
  28. package/dist/runner/common/jsonrpc/client.d.ts +29 -0
  29. package/dist/runner/common/jsonrpc/client.js +43 -12
  30. package/dist/runner/common/spawning-runner.d.ts +5 -0
  31. package/dist/runner/common/spawning-runner.js +8 -3
  32. package/dist/runner/common/turn-started.d.ts +8 -0
  33. package/dist/runner/common/turn-started.js +11 -0
  34. package/dist/runner/dsh/approval.js +84 -51
  35. package/dist/runner/dsh/client.d.ts +7 -9
  36. package/dist/runner/dsh/client.js +356 -287
  37. package/dist/runner/dsh/mux-stream.js +28 -8
  38. package/dist/runner/dsh/runner.d.ts +4 -3
  39. package/dist/runner/dsh/runner.js +88 -64
  40. package/dist/runner/dsh/translator.d.ts +1 -31
  41. package/dist/runner/dsh/translator.js +180 -178
  42. package/dist/runner/kimi/acp/runner.js +81 -44
  43. package/dist/runner/opencode/acp/error-monitor.js +5 -5
  44. package/dist/runner/pi/rpc/client.js +4 -13
  45. package/dist/runner/pi/rpc/translator.js +61 -60
  46. package/dist/session/claude/sessions.js +113 -101
  47. package/dist/session/codex/rollout-reader.js +179 -180
  48. package/dist/session/dsh/sessions.js +281 -231
  49. package/dist/session/kimi/sessions.d.ts +0 -7
  50. package/dist/session/kimi/sessions.js +129 -109
  51. package/dist/session/opencode/sessions.d.ts +0 -11
  52. package/dist/session/opencode/sessions.js +161 -137
  53. package/dist/session/session-store.d.ts +8 -0
  54. package/dist/session/session-store.js +98 -98
  55. package/package.json +1 -1
@@ -1,5 +1,28 @@
1
1
  import { getLogger } from '../../logger/index.js';
2
2
  import { DshError } from './errors.js';
3
+ function nonEmptyString(value) {
4
+ return typeof value === 'string' && value ? value : undefined;
5
+ }
6
+ // 模块级命名提取器:顺序即优先级,先命中先返回。抽到模块顶层后
7
+ // describeSocketError 只剩「校验 + 遍历」,决策点集中在独立的纯函数里。
8
+ function messageDetail(e) {
9
+ return nonEmptyString(e.message);
10
+ }
11
+ function errorObjectDetail(e) {
12
+ return e.error instanceof Error ? nonEmptyString(e.error.message) : undefined;
13
+ }
14
+ function errorStringDetail(e) {
15
+ return nonEmptyString(e.error);
16
+ }
17
+ function typeDetail(e) {
18
+ return nonEmptyString(e.type);
19
+ }
20
+ const SOCKET_ERROR_DETAIL_EXTRACTORS = [
21
+ messageDetail,
22
+ errorObjectDetail,
23
+ errorStringDetail,
24
+ typeDetail,
25
+ ];
3
26
  /**
4
27
  * onerror 的事件对象在 ws 实现之间形态不一({message} / {error} / {type})。
5
28
  * 尽力取出可读描述;取不到就不追加,别拿 '[object Object]' 充数。
@@ -8,14 +31,11 @@ function describeSocketError(event) {
8
31
  if (!event || typeof event !== 'object')
9
32
  return undefined;
10
33
  const e = event;
11
- if (typeof e.message === 'string' && e.message)
12
- return e.message;
13
- if (e.error instanceof Error && e.error.message)
14
- return e.error.message;
15
- if (typeof e.error === 'string' && e.error)
16
- return e.error;
17
- if (typeof e.type === 'string' && e.type)
18
- return e.type;
34
+ for (const extract of SOCKET_ERROR_DETAIL_EXTRACTORS) {
35
+ const detail = extract(e);
36
+ if (detail)
37
+ return detail;
38
+ }
19
39
  return undefined;
20
40
  }
21
41
  export async function* openMuxStream(opts) {
@@ -21,6 +21,7 @@ import type { AgentSessionReader } from '../../session-contract.js';
21
21
  import type { AgentEvent, AgentStatusInfo, BackgroundJobRoster, SpawnOptions, StopOutcome } from '../types.js';
22
22
  import type { AgentRunner } from '../types.js';
23
23
  import { DshClient, type WebSocketFactory } from './client.js';
24
+ import { DshApprovalTracker } from './approval.js';
24
25
  import type { DshDaemon } from './daemon.js';
25
26
  export interface DshRunnerOptions {
26
27
  kind: 'dsh';
@@ -110,7 +111,7 @@ export declare class DshRunner implements AgentRunner {
110
111
  /** 已对齐权限预设的 session;同一 session 只执行一次 `/permission`。 */
111
112
  private readonly permissionAlignedSessions;
112
113
  /** `$events` approval waterfall state for the current run. */
113
- private readonly approvalTracker;
114
+ readonly approvalTracker: DshApprovalTracker;
114
115
  /**
115
116
  * eventId → 待回答的 DSH `ask_user_question` waterfall。
116
117
  *
@@ -207,7 +208,7 @@ export declare class DshRunner implements AgentRunner {
207
208
  * 现,卡片列出来只会误导"还没跑完"。`job/list` 拿不到时返回空数组——没有
208
209
  * 任务列表不等于有任务在跑。
209
210
  */
210
- private collectBackgroundJobs;
211
+ collectBackgroundJobs(sessionId: string, signal: AbortSignal): Promise<AgentEvent[]>;
211
212
  /**
212
213
  * 订阅本 session 的后台任务名单(跨 turn 生命周期)。
213
214
  *
@@ -243,7 +244,7 @@ export declare class DshRunner implements AgentRunner {
243
244
  * the Other input, multi-select submit and skip from this view alone.
244
245
  */
245
246
  private buildQuestionRequested;
246
- private approvalResolved;
247
+ approvalResolved(requestId: string): AgentEvent;
247
248
  private approvalFallbackText;
248
249
  /**
249
250
  * Compact the session through DSH's `/compact` command.
@@ -192,6 +192,88 @@ function interruptedResult(sessionId) {
192
192
  errorMessage: 'run stopped',
193
193
  };
194
194
  }
195
+ /** goal/change:权威变更,不等投影直接按事件更新(create/edit/pause/...)。 */
196
+ function goalChangeEvents(_runner, event, ctx) {
197
+ const parsed = goalFromChange(event.data);
198
+ if (parsed !== undefined && !sameGoal(parsed, ctx.goal.last)) {
199
+ ctx.goal.last = parsed;
200
+ return [goalStateEvent(parsed, true)];
201
+ }
202
+ return [];
203
+ }
204
+ /** approval/decided:审批回流,清审批卡。 */
205
+ function approvalDecidedEvents(runner, event) {
206
+ const data = event.data;
207
+ if (!data.id)
208
+ return [];
209
+ runner.approvalTracker.onDecided(data.id);
210
+ return [runner.approvalResolved(data.id)];
211
+ }
212
+ /** 终态分支查表:命中即返回(不再落到 translator)。 */
213
+ const SESSION_EVENT_TERMINAL_HANDLERS = new Map([
214
+ ['goal/change', goalChangeEvents],
215
+ ['approval/decided', approvalDecidedEvents],
216
+ ]);
217
+ /** tool/call:先记账(审批卡命令展示用),随后仍落到 translator 出工具行。 */
218
+ function recordToolCall(toolCalls, event) {
219
+ const data = event.data;
220
+ if (data.callId && data.name) {
221
+ toolCalls.set(data.callId, {
222
+ name: data.name,
223
+ arguments: data.arguments ?? '',
224
+ ...(data.turn === undefined ? {} : { turn: data.turn }),
225
+ });
226
+ if (toolCalls.size > 100) {
227
+ const oldest = toolCalls.keys().next().value;
228
+ if (oldest !== undefined)
229
+ toolCalls.delete(oldest);
230
+ }
231
+ }
232
+ }
233
+ /** result 收尾:后台任务名单 + goal 收尾复核;返回要插到 result 前的事件。 */
234
+ async function resultFinalizeEvents(runner, ctx) {
235
+ const events = [];
236
+ for (const jobsEvent of await runner.collectBackgroundJobs(ctx.sessionId, ctx.abort.signal)) {
237
+ events.push(jobsEvent);
238
+ }
239
+ // 收尾复核:这一轮结束时 goal 是否仍 active,决定卡片能不能收尾。
240
+ // 必须现读宿主,不能沿用过程中的最后一次观测——goal 可能在本轮
241
+ // 最后一个 step 才被 complete,也可能刚被 driver 置为 blocked。
242
+ const finalGoal = await runner.client.goalState(ctx.sessionId, {
243
+ signal: ctx.signal,
244
+ timeoutMs: GOAL_PROBE_TIMEOUT_MS,
245
+ });
246
+ if (!finalGoal.known) {
247
+ // 收尾读不到:seed 已报过 unknown 就不重复发,否则把状态降级
248
+ // 交给卡片(卡片据此不再宣布完成)。
249
+ if (ctx.goal.known) {
250
+ ctx.goal.known = false;
251
+ events.push(goalStateEvent(null, false));
252
+ }
253
+ }
254
+ else if (!ctx.goal.known || !sameGoal(finalGoal.goal, ctx.goal.last)) {
255
+ ctx.goal.known = true;
256
+ ctx.goal.last = finalGoal.goal;
257
+ events.push(goalStateEvent(finalGoal.goal, true));
258
+ }
259
+ return events;
260
+ }
261
+ /** translator 循环:result 事件插入收尾事件并提前返回。 */
262
+ async function collectAgentEvents(runner, event, ctx) {
263
+ const events = [];
264
+ for (const ev of ctx.translator.eventToAgentEvents(event, ctx.sessionId)) {
265
+ if (ev.type === 'result') {
266
+ if (!ctx.abort.signal.aborted) {
267
+ for (const finalized of await resultFinalizeEvents(runner, ctx))
268
+ events.push(finalized);
269
+ }
270
+ events.push(attachContextLimit(ev, ctx.live.contextLimit));
271
+ return events;
272
+ }
273
+ events.push(ev);
274
+ }
275
+ return events;
276
+ }
195
277
  export class DshRunner {
196
278
  kind = 'dsh';
197
279
  /**
@@ -772,75 +854,17 @@ export class DshRunner {
772
854
  * 复核)再随 result 一起返回。
773
855
  */
774
856
  async handleSessionEvent(event, sessionId, translator, goal, live, abort, signal) {
775
- if (event.type === 'goal/change') {
776
- // 权威变更(create/edit/pause/resume/complete/block/clear):不等投影
777
- // 推送,直接按事件更新——agent 刚 create 的那一刻卡片就该显示目标。
778
- const parsed = goalFromChange(event.data);
779
- if (parsed !== undefined && !sameGoal(parsed, goal.last)) {
780
- goal.last = parsed;
781
- return [goalStateEvent(parsed, true)];
782
- }
783
- return [];
784
- }
857
+ const ctx = { sessionId, translator, goal, live, abort, signal };
858
+ const terminal = SESSION_EVENT_TERMINAL_HANDLERS.get(event.type);
859
+ if (terminal)
860
+ return terminal(this, event, ctx);
785
861
  if (event.type === 'tool/call') {
786
- const data = event.data;
787
- if (data.callId && data.name) {
788
- this.toolCalls.set(data.callId, {
789
- name: data.name,
790
- arguments: data.arguments ?? '',
791
- ...(data.turn === undefined ? {} : { turn: data.turn }),
792
- });
793
- if (this.toolCalls.size > 100) {
794
- const oldest = this.toolCalls.keys().next().value;
795
- if (oldest !== undefined)
796
- this.toolCalls.delete(oldest);
797
- }
798
- }
799
862
  // 记账后继续往下走:tool/call 同时是卡片工具行的来源(translator 翻
800
863
  // 译成 assistant.tool_use)。早期版本在这里 `continue`,把调用轨迹整个
801
864
  // 吞掉——卡片因此只有正文、没有任何工具行。
865
+ recordToolCall(this.toolCalls, event);
802
866
  }
803
- else if (event.type === 'approval/decided') {
804
- const data = event.data;
805
- if (!data.id)
806
- return [];
807
- this.approvalTracker.onDecided(data.id);
808
- return [this.approvalResolved(data.id)];
809
- }
810
- const events = [];
811
- for (const ev of translator.eventToAgentEvents(event, sessionId)) {
812
- if (ev.type === 'result') {
813
- if (!abort.signal.aborted) {
814
- for (const jobsEvent of await this.collectBackgroundJobs(sessionId, abort.signal)) {
815
- events.push(jobsEvent);
816
- }
817
- // 收尾复核:这一轮结束时 goal 是否仍 active,决定卡片能不能收尾。
818
- // 必须现读宿主,不能沿用过程中的最后一次观测——goal 可能在本轮
819
- // 最后一个 step 才被 complete,也可能刚被 driver 置为 blocked。
820
- const finalGoal = await this.client.goalState(sessionId, {
821
- signal,
822
- timeoutMs: GOAL_PROBE_TIMEOUT_MS,
823
- });
824
- if (!finalGoal.known) {
825
- // 收尾读不到:seed 已报过 unknown 就不重复发,否则把状态降级
826
- // 交给卡片(卡片据此不再宣布完成)。
827
- if (goal.known) {
828
- goal.known = false;
829
- events.push(goalStateEvent(null, false));
830
- }
831
- }
832
- else if (!goal.known || !sameGoal(finalGoal.goal, goal.last)) {
833
- goal.known = true;
834
- goal.last = finalGoal.goal;
835
- events.push(goalStateEvent(finalGoal.goal, true));
836
- }
837
- }
838
- events.push(attachContextLimit(ev, live.contextLimit));
839
- return events;
840
- }
841
- events.push(ev);
842
- }
843
- return events;
867
+ return collectAgentEvents(this, event, ctx);
844
868
  }
845
869
  /**
846
870
  * 收尾时仍在跑的后台任务。
@@ -26,39 +26,9 @@
26
26
  import type { AgentEvent, ResultEvent } from '../types.js';
27
27
  import type { DshAssistantStreamFrame, DshSessionEvent, DshTokenUsage } from '../../dsh/types.js';
28
28
  export declare class DshTranslator {
29
- /** 本 turn 累计用量:DSH 每次模型调用(step)一条 usage,工具往返 = 多条。 */
30
- private turnUsage;
31
- /** 末次采样的 usage:上下文水位,或只有 live chunk 时的兜底值。 */
32
- private lastUsage?;
33
- /** 是否收到过 durable assistant/message.usage(唯一累加来源)。 */
34
- private hasMessageUsage;
35
- /** 是否只收到过 live usage chunk(无 durable message usage 时的兜底)。 */
36
- private hasStreamUsage;
37
- /** 当前 attempt 的 id(start 帧携带,chunk 帧也带;断开重连重放时不变)。 */
38
- private attemptId;
39
- /**
40
- * 本 turn 末条 durable assistant 消息的正文(**完整**、未截断)。
41
- *
42
- * live chunk 是逐 delta 拼接、会被状态层的 MAX_TEXT_CHARS 窗口截断;
43
- * durable `assistant/message` 一次就到齐全文。收 turn 时把它挂在 result 上,
44
- * 卡片才能渲染完整回答。
45
- */
46
- private lastMessageText?;
29
+ private readonly state;
47
30
  /** Translate one live assistant-stream chunk into AgentEvents. */
48
31
  streamFrameToAgentEvents(frame: DshAssistantStreamFrame, _sessionId: string): AgentEvent[];
49
- /**
50
- * Record durable per-step usage — the accumulation source. assistant/message
51
- * carries exactly one usage per model call and is seq-deduped by the client,
52
- * so summing it gives the turn total without double counting.
53
- */
54
- private recordMessageUsage;
55
- /**
56
- * Record a live usage chunk. Never summed: the same step also arrives as a
57
- * durable assistant/message.usage, so summing both would double count. It only
58
- * refreshes the watermark and covers a turn whose messages never persisted.
59
- */
60
- private recordStreamUsage;
61
- private chunkToAgentEvents;
62
32
  /** Translate one session event into AgentEvents (may be empty). */
63
33
  eventToAgentEvents(ev: DshSessionEvent, sessionId: string): AgentEvent[];
64
34
  }