lark-remote 0.5.12 → 0.5.13

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 (95) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/README.en.md +1 -14
  3. package/README.md +1 -14
  4. package/dist/bridge/approval-coordinator.d.ts +1 -0
  5. package/dist/bridge/approval-coordinator.js +12 -24
  6. package/dist/bridge/index.d.ts +7 -0
  7. package/dist/bridge/index.js +77 -227
  8. package/dist/bridge/run-approval.d.ts +15 -0
  9. package/dist/bridge/run-approval.js +41 -0
  10. package/dist/bridge/run-usage.d.ts +46 -0
  11. package/dist/bridge/run-usage.js +96 -0
  12. package/dist/bridge/session-writeback.d.ts +18 -0
  13. package/dist/bridge/session-writeback.js +36 -0
  14. package/dist/card/bash-card-session.js +2 -5
  15. package/dist/card/card-session.d.ts +2 -0
  16. package/dist/card/card-session.js +20 -22
  17. package/dist/card/card-shared.d.ts +4 -4
  18. package/dist/card/card-shared.js +3 -9
  19. package/dist/card/collapsible.js +3 -2
  20. package/dist/card/final-answer-overflow.d.ts +7 -0
  21. package/dist/card/final-answer-overflow.js +46 -0
  22. package/dist/card/run-card-session.js +2 -7
  23. package/dist/card/run-renderer.d.ts +14 -0
  24. package/dist/card/run-renderer.js +119 -1
  25. package/dist/card/run-state.d.ts +12 -1
  26. package/dist/card/run-state.js +73 -34
  27. package/dist/config/codex-config.js +4 -4
  28. package/dist/config/index.d.ts +4 -2
  29. package/dist/config/index.js +6 -4
  30. package/dist/inbound/turn-assembler.js +6 -0
  31. package/dist/index.js +15 -409
  32. package/dist/message-handlers.d.ts +26 -0
  33. package/dist/message-handlers.js +275 -0
  34. package/dist/router/card-action-payload.d.ts +1 -1
  35. package/dist/router/card-action-payload.js +1 -1
  36. package/dist/router/commands/ls.d.ts +9 -0
  37. package/dist/router/commands/ls.js +243 -0
  38. package/dist/router/commands/order.d.ts +12 -0
  39. package/dist/router/commands/order.js +178 -0
  40. package/dist/router/commands/resume.d.ts +47 -0
  41. package/dist/router/commands/resume.js +246 -0
  42. package/dist/router/commands/ws.d.ts +15 -0
  43. package/dist/router/commands/ws.js +189 -0
  44. package/dist/router/config/claude.js +3 -3
  45. package/dist/router/config/codex.js +2 -6
  46. package/dist/router/config/common/turn-idle.d.ts +2 -0
  47. package/dist/router/config/common/turn-idle.js +9 -0
  48. package/dist/router/config/kimi.js +2 -6
  49. package/dist/router/config/opencode.js +2 -6
  50. package/dist/router/index.d.ts +14 -22
  51. package/dist/router/index.js +70 -1152
  52. package/dist/router/paging.d.ts +13 -0
  53. package/dist/router/paging.js +20 -0
  54. package/dist/router/path-validation.d.ts +27 -0
  55. package/dist/router/path-validation.js +45 -0
  56. package/dist/runner/claude/runner.d.ts +3 -4
  57. package/dist/runner/claude/runner.js +4 -5
  58. package/dist/runner/claude/session.js +1 -1
  59. package/dist/runner/codex/app-server/translator.d.ts +2 -0
  60. package/dist/runner/codex/app-server/translator.js +65 -9
  61. package/dist/runner/common/acp/base-translator.d.ts +3 -8
  62. package/dist/runner/common/acp/base-translator.js +16 -7
  63. package/dist/runner/common/spawning-runner.js +10 -4
  64. package/dist/runner/common/turn-started.d.ts +2 -0
  65. package/dist/runner/common/turn-started.js +10 -0
  66. package/dist/runner/dsh/auth.js +2 -3
  67. package/dist/runner/dsh/client.d.ts +1 -6
  68. package/dist/runner/dsh/client.js +11 -165
  69. package/dist/runner/dsh/daemon.d.ts +0 -2
  70. package/dist/runner/dsh/daemon.js +1 -6
  71. package/dist/runner/dsh/errors.d.ts +5 -0
  72. package/dist/runner/dsh/errors.js +11 -0
  73. package/dist/runner/dsh/exit-cancel.d.ts +0 -2
  74. package/dist/runner/dsh/exit-cancel.js +1 -1
  75. package/dist/runner/dsh/home.d.ts +2 -0
  76. package/dist/runner/dsh/home.js +8 -0
  77. package/dist/runner/dsh/inflight.d.ts +0 -2
  78. package/dist/runner/dsh/inflight.js +1 -1
  79. package/dist/runner/dsh/mux-stream.d.ts +26 -0
  80. package/dist/runner/dsh/mux-stream.js +157 -0
  81. package/dist/runner/dsh/translator.d.ts +0 -10
  82. package/dist/runner/dsh/translator.js +1 -1
  83. package/dist/runner/index.d.ts +1 -1
  84. package/dist/runner/kimi/acp/translator.js +1 -12
  85. package/dist/runner/opencode/acp/translator.js +1 -12
  86. package/dist/runner/pi/rpc/translator.d.ts +2 -8
  87. package/dist/runner/pi/rpc/translator.js +2 -7
  88. package/dist/runner/registry.d.ts +0 -19
  89. package/dist/runner/registry.js +0 -32
  90. package/dist/runner/types.d.ts +17 -1
  91. package/dist/session/claude/session-index.js +4 -2
  92. package/dist/session/kimi/sessions.js +2 -2
  93. package/dist/session/opencode/sessions.js +2 -1
  94. package/dist/session/pi/sessions.js +3 -2
  95. package/package.json +1 -1
@@ -0,0 +1,5 @@
1
+ export declare class DshError extends Error {
2
+ readonly code: string;
3
+ readonly details: Record<string, unknown>;
4
+ constructor(code: string, message: string, details?: Record<string, unknown>);
5
+ }
@@ -0,0 +1,11 @@
1
+ export class DshError extends Error {
2
+ code;
3
+ details;
4
+ constructor(code, message, details = {}) {
5
+ super(message);
6
+ this.name = 'DshError';
7
+ this.code = code;
8
+ this.details = details;
9
+ }
10
+ }
11
+ //# sourceMappingURL=errors.js.map
@@ -12,8 +12,6 @@
12
12
  * curl transport the session reader already uses: bounded by `timeoutMs` and
13
13
  * never throws.
14
14
  */
15
- /** Upper bound on the exit-path cancel:退出不能被一个卡住的 host 拖住。 */
16
- export declare const EXIT_CANCEL_TIMEOUT_MS = 2000;
17
15
  export interface SyncCancelOptions {
18
16
  baseUrl: string;
19
17
  sessionId: string;
@@ -20,7 +20,7 @@ import { isWin32 } from '../../platform/select.js';
20
20
  import { spawnProcessSync } from '../../platform/spawn.js';
21
21
  import { DSH_COOKIE_LIFETIME_MS, mintDshBrowserCookie, readDshBrowserSecretSync, resolveDshCredentialsFile, } from './auth.js';
22
22
  /** Upper bound on the exit-path cancel:退出不能被一个卡住的 host 拖住。 */
23
- export const EXIT_CANCEL_TIMEOUT_MS = 2_000;
23
+ const EXIT_CANCEL_TIMEOUT_MS = 2_000;
24
24
  /**
25
25
  * Fire one `session/cancel` synchronously. Never throws: a failed cancel must
26
26
  * not block the exit path or mask the real exit reason.
@@ -0,0 +1,2 @@
1
+ /** Resolve the same DSH home DSH itself uses: configured, env, then ~/.dsh. */
2
+ export declare function resolveDshHome(configured?: string, env?: NodeJS.ProcessEnv): string;
@@ -0,0 +1,8 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ /** Resolve the same DSH home DSH itself uses: configured, env, then ~/.dsh. */
4
+ export function resolveDshHome(configured, env = process.env) {
5
+ const value = configured?.trim() || env.DSH_HOME?.trim();
6
+ return path.resolve(value && value.length > 0 ? value : path.join(os.homedir(), '.dsh'));
7
+ }
8
+ //# sourceMappingURL=home.js.map
@@ -58,5 +58,3 @@ export interface DshInflightReconcileResult {
58
58
  * entries are left in the file so a later startup can retry.
59
59
  */
60
60
  export declare function reconcileInflightDshRuns(opts: DshInflightReconcileOptions): Promise<DshInflightReconcileResult>;
61
- /** One short owner-facing notice; session ids are truncated, not paths. */
62
- export declare function formatInflightWarning(entries: DshInflightEntry[]): string;
@@ -126,7 +126,7 @@ export async function reconcileInflightDshRuns(opts) {
126
126
  return { removed, open: open.length, skipped: false };
127
127
  }
128
128
  /** One short owner-facing notice; session ids are truncated, not paths. */
129
- export function formatInflightWarning(entries) {
129
+ function formatInflightWarning(entries) {
130
130
  const lines = entries.slice(0, 3).map((entry) => {
131
131
  const id = entry.sessionId.length > 8 ? entry.sessionId.slice(0, 8) : entry.sessionId;
132
132
  return `- session ${id}(${entry.cwd ?? '未知目录'})`;
@@ -0,0 +1,26 @@
1
+ interface MuxSocket {
2
+ onopen: ((event: unknown) => void) | null;
3
+ onmessage: ((event: {
4
+ data: unknown;
5
+ }) => void) | null;
6
+ onerror: ((event: unknown) => void) | null;
7
+ onclose: ((event: unknown) => void) | null;
8
+ onunexpectedresponse?: ((event: {
9
+ statusCode?: number;
10
+ }) => void) | null;
11
+ send(data: string): void;
12
+ close(): void;
13
+ }
14
+ export interface OpenMuxStreamOptions {
15
+ baseUrl: string;
16
+ endpoint: string;
17
+ payload: unknown;
18
+ signal: AbortSignal;
19
+ requestTimeoutMs: number;
20
+ authCookie(): Promise<string | undefined>;
21
+ webSocketFactory(url: string, options?: {
22
+ headers?: Record<string, string>;
23
+ }): MuxSocket;
24
+ }
25
+ export declare function openMuxStream(opts: OpenMuxStreamOptions): AsyncGenerator<unknown>;
26
+ export {};
@@ -0,0 +1,157 @@
1
+ import { getLogger } from '../../logger/index.js';
2
+ import { DshError } from './errors.js';
3
+ export async function* openMuxStream(opts) {
4
+ const cookie = await opts.authCookie();
5
+ const socket = opts.webSocketFactory(`${opts.baseUrl.replace(/^http/, 'ws')}/api/remote.mux`, cookie ? { headers: { cookie } } : undefined);
6
+ const streamId = `lr-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
7
+ const queue = [];
8
+ let waiter = null;
9
+ let ended = false;
10
+ let failure;
11
+ let opened = false;
12
+ const settle = (frame) => {
13
+ if (waiter) {
14
+ const resolve = waiter;
15
+ waiter = null;
16
+ resolve(frame);
17
+ return;
18
+ }
19
+ if (frame)
20
+ queue.push(frame);
21
+ else
22
+ ended = true;
23
+ };
24
+ const connectTimer = setTimeout(() => {
25
+ if (opened || ended)
26
+ return;
27
+ failure = new DshError('transport', 'DSH remote mux WebSocket connect timeout');
28
+ ended = true;
29
+ settle(undefined);
30
+ try {
31
+ socket.close();
32
+ }
33
+ catch {
34
+ /* ignore close error */
35
+ }
36
+ }, opts.requestTimeoutMs);
37
+ connectTimer.unref?.();
38
+ socket.onopen = () => {
39
+ opened = true;
40
+ clearTimeout(connectTimer);
41
+ if (ended)
42
+ return;
43
+ try {
44
+ socket.send(JSON.stringify({
45
+ type: 'open',
46
+ streamId,
47
+ endpoint: opts.endpoint,
48
+ payload: opts.payload,
49
+ }));
50
+ }
51
+ catch (err) {
52
+ failure = new DshError('transport', `DSH remote mux send error: ${err.message}`);
53
+ ended = true;
54
+ settle(undefined);
55
+ }
56
+ };
57
+ socket.onmessage = (event) => {
58
+ if (ended)
59
+ return;
60
+ const text = typeof event.data === 'string'
61
+ ? event.data
62
+ : Buffer.isBuffer(event.data)
63
+ ? event.data.toString('utf8')
64
+ : String(event.data);
65
+ let message;
66
+ try {
67
+ message = JSON.parse(text);
68
+ }
69
+ catch {
70
+ getLogger().warn('[dsh] dropped malformed remote mux frame');
71
+ return;
72
+ }
73
+ if (message.streamId !== streamId)
74
+ return;
75
+ if (message.type === 'item' && message.value) {
76
+ settle(message.value);
77
+ return;
78
+ }
79
+ if (message.type === 'end') {
80
+ ended = true;
81
+ settle(undefined);
82
+ return;
83
+ }
84
+ if (message.type === 'error') {
85
+ failure = new DshError(message.error?.code ?? 'stream', message.error?.message ?? 'DSH remote stream failed', message.error?.details ?? {});
86
+ ended = true;
87
+ settle(undefined);
88
+ }
89
+ };
90
+ socket.onerror = () => {
91
+ if (ended)
92
+ return;
93
+ failure = new DshError('transport', 'DSH remote mux WebSocket error');
94
+ ended = true;
95
+ settle(undefined);
96
+ };
97
+ socket.onunexpectedresponse = (event) => {
98
+ if (ended)
99
+ return;
100
+ const status = event.statusCode;
101
+ failure = new DshError('http', `DSH remote mux WebSocket HTTP ${status === undefined ? 'error' : status}`, status === undefined ? {} : { status });
102
+ ended = true;
103
+ settle(undefined);
104
+ };
105
+ socket.onclose = () => {
106
+ if (ended)
107
+ return;
108
+ ended = true;
109
+ settle(undefined);
110
+ };
111
+ const onAbort = () => {
112
+ if (ended)
113
+ return;
114
+ ended = true;
115
+ settle(undefined);
116
+ try {
117
+ socket.close();
118
+ }
119
+ catch {
120
+ /* ignore close error */
121
+ }
122
+ };
123
+ opts.signal.addEventListener('abort', onAbort, { once: true });
124
+ const next = () => new Promise((resolve) => {
125
+ if (queue.length > 0) {
126
+ resolve(queue.shift());
127
+ return;
128
+ }
129
+ if (ended) {
130
+ resolve(undefined);
131
+ return;
132
+ }
133
+ waiter = resolve;
134
+ });
135
+ try {
136
+ while (!opts.signal.aborted) {
137
+ const frame = await next();
138
+ if (frame === undefined) {
139
+ if (failure && !opts.signal.aborted)
140
+ throw failure;
141
+ return;
142
+ }
143
+ yield frame;
144
+ }
145
+ }
146
+ finally {
147
+ clearTimeout(connectTimer);
148
+ opts.signal.removeEventListener('abort', onAbort);
149
+ try {
150
+ socket.close();
151
+ }
152
+ catch {
153
+ /* ignore close error */
154
+ }
155
+ }
156
+ }
157
+ //# sourceMappingURL=mux-stream.js.map
@@ -25,13 +25,6 @@
25
25
  */
26
26
  import type { AgentEvent, ResultEvent } from '../types.js';
27
27
  import type { DshAssistantStreamFrame, DshSessionEvent, DshTokenUsage } from './types.js';
28
- /** 本 turn 各采样用量之和(input 是非缓存输入,与 ccusage/ResultEvent 口径一致)。 */
29
- interface UsageTotals {
30
- input: number;
31
- output: number;
32
- cacheRead: number;
33
- cacheWrite: number;
34
- }
35
28
  export declare class DshTranslator {
36
29
  /** 本 turn 累计用量:DSH 每次模型调用(step)一条 usage,工具往返 = 多条。 */
37
30
  private turnUsage;
@@ -63,6 +56,3 @@ export declare class DshTranslator {
63
56
  }
64
57
  /** Map DSH TokenUsage → ResultEvent.usage (cacheWrite = cache creation). */
65
58
  export declare function mapUsage(u: DshTokenUsage): NonNullable<ResultEvent['usage']>;
66
- /** Map a turn's summed usage → ResultEvent.usage (cacheWrite = cache creation). */
67
- export declare function mapAggregatedUsage(t: UsageTotals): NonNullable<ResultEvent['usage']>;
68
- export {};
@@ -265,7 +265,7 @@ export function mapUsage(u) {
265
265
  });
266
266
  }
267
267
  /** Map a turn's summed usage → ResultEvent.usage (cacheWrite = cache creation). */
268
- export function mapAggregatedUsage(t) {
268
+ function mapAggregatedUsage(t) {
269
269
  return {
270
270
  input_tokens: t.input,
271
271
  output_tokens: t.output,
@@ -15,6 +15,6 @@ export { KimiAcpRunner } from './kimi/index.js';
15
15
  export { DshRunner } from './dsh/index.js';
16
16
  export { BashProcessRunner, type BashRunner } from './bash/index.js';
17
17
  export { AgentRegistry } from './registry.js';
18
- export type { ResultEvent, PlanEvent, FileChangeEvent, AgentEvent, Runner, RunnerLifetime, AgentKind, AgentSession, AgentSessionContentEvent, AgentSessionUsage, SessionContent, SessionSummary, AgentSessionReader, AgentRunner, TurnStartedEvent, TurnDiffEvent, NoticeEvent, SessionInfoEvent, ToolHint, ApprovalView, ApprovalRequestedEvent, ApprovalResolvedEvent, GoalView, GoalStateEvent, } from './types.js';
18
+ export type { ResultEvent, PlanEvent, FileChangeEvent, AgentEvent, Runner, RunnerLifetime, AgentKind, AgentSession, AgentSessionContentEvent, AgentSessionUsage, SessionContent, SessionSummary, AgentSessionReader, AgentRunner, TurnStartedEvent, TurnDiffEvent, FinalAnswerEvent, NoticeEvent, SessionInfoEvent, ToolHint, ApprovalView, ApprovalRequestedEvent, ApprovalResolvedEvent, GoalView, GoalStateEvent, } from './types.js';
19
19
  export { resolveAgentChoices } from './resolve-agent-choices.js';
20
20
  export { syncAgentChoices } from './sync-agent-choices.js';
@@ -159,18 +159,7 @@ export class KimiAcpTranslator extends BaseAcpTranslator {
159
159
  // 才提供「本会话总是允许」(acceptForSession);否则与旧行为一致。
160
160
  availableDecisions: deriveAcpAvailableDecisions(params.options ?? []),
161
161
  };
162
- return [
163
- {
164
- type: 'approval_requested',
165
- requestId,
166
- kind: 'command',
167
- threadId: params.sessionId,
168
- turnId: '',
169
- itemId: '',
170
- view,
171
- timestamp: new Date().toISOString(),
172
- },
173
- ];
162
+ return this.approvalRequestedEvents(requestId, params.sessionId, view);
174
163
  }
175
164
  handleOtherServerRequest(requestId, method, params) {
176
165
  if (method !== ServerRequestMethod.ELICITATION_CREATE) {
@@ -189,18 +189,7 @@ export class OpencodeAcpTranslator extends BaseAcpTranslator {
189
189
  // 才提供「本会话总是允许」(acceptForSession)。
190
190
  availableDecisions: deriveAcpAvailableDecisions(params.options ?? []),
191
191
  };
192
- return [
193
- {
194
- type: 'approval_requested',
195
- requestId,
196
- kind: 'command',
197
- threadId: params.sessionId,
198
- turnId: '',
199
- itemId: '',
200
- view,
201
- timestamp: new Date().toISOString(),
202
- },
203
- ];
192
+ return this.approvalRequestedEvents(requestId, params.sessionId, view);
204
193
  }
205
194
  }
206
195
  // =============================================================================
@@ -10,15 +10,9 @@
10
10
  * `result` from that response.
11
11
  * - `agent_settled` is ignored for compaction turns (no double result).
12
12
  */
13
- import type { AgentEvent, ResultEvent } from '../../types.js';
13
+ import type { AgentEvent, ResultEvent, TurnStartedEvent } from '../../types.js';
14
14
  import { type PiRpcEvent, type PiRpcResponse } from './protocol-types.js';
15
- export interface PiRpcTurnStartedEvent {
16
- type: 'turn_started';
17
- threadId: string;
18
- turnId: string;
19
- operationKind: 'turn' | 'compaction';
20
- timestamp?: string;
21
- }
15
+ export type PiRpcTurnStartedEvent = TurnStartedEvent;
22
16
  export type PiRpcTranslatorEvent = AgentEvent | PiRpcTurnStartedEvent;
23
17
  export declare class PiRpcTranslator {
24
18
  private content;
@@ -10,6 +10,7 @@
10
10
  * `result` from that response.
11
11
  * - `agent_settled` is ignored for compaction turns (no double result).
12
12
  */
13
+ import { makeTurnStartedEvent } from '../../common/turn-started.js';
13
14
  export class PiRpcTranslator {
14
15
  content = [];
15
16
  currentContentIndex = -1;
@@ -135,13 +136,7 @@ export class PiRpcTranslator {
135
136
  /** Self-produced turn_started (no wire equivalent). */
136
137
  produceTurnStarted(sessionId, turnId) {
137
138
  this.sessionId = sessionId;
138
- return {
139
- type: 'turn_started',
140
- threadId: sessionId,
141
- turnId,
142
- operationKind: this.operationKind === 'compact' ? 'compaction' : 'turn',
143
- timestamp: new Date().toISOString(),
144
- };
139
+ return makeTurnStartedEvent(sessionId, turnId, this.operationKind);
145
140
  }
146
141
  /** Result for a completed normal turn (agent_settled). */
147
142
  produceResultFromSettled() {
@@ -14,28 +14,9 @@ import type { AgentKind, Runner } from './index.js';
14
14
  */
15
15
  export declare class AgentRegistry {
16
16
  private readonly factories;
17
- private readonly displayNames;
18
17
  /** Container for runtime config updates (pi provider dynamic reload). */
19
18
  private configContainer?;
20
19
  register(kind: AgentKind, factory: (cwd: string) => Runner): void;
21
- /**
22
- * 注册 agent 的显示名。应在 register(kind, factory) 后调用。
23
- * 用于 agentDisplayName 等全局查询,实现单点真相。
24
- */
25
- registerDisplayName(kind: AgentKind, displayName: string): void;
26
- /**
27
- * 获取 agent 显示名。未注册时返回原始 kind。
28
- */
29
- getDisplayName(kind: AgentKind): string;
30
- /**
31
- * 设置全局 registry 实例,供 agentDisplayName 等全局函数使用。
32
- * 在 index.ts 初始化所有 agents 后调用。
33
- */
34
- static setGlobalInstance(reg: AgentRegistry | undefined): void;
35
- /**
36
- * 获取全局 registry 实例。
37
- */
38
- static getGlobalInstance(): AgentRegistry | undefined;
39
20
  /**
40
21
  * Set config container reference. Call after bridge config is updated.
41
22
  * Factory functions can read latest config from here.
@@ -1,8 +1,3 @@
1
- /**
2
- * 单例 registry 实例,供 agentDisplayName 等全局查询使用。
3
- * 在 index.ts 初始化后设置。
4
- */
5
- let globalRegistry;
6
1
  /**
7
2
  * Registry of agent factories, keyed by `AgentKind`. `index.ts` registers one
8
3
  * factory per available agent at startup; `Bridge.getRunner(cwd)` looks
@@ -18,38 +13,11 @@ let globalRegistry;
18
13
  */
19
14
  export class AgentRegistry {
20
15
  factories = new Map();
21
- displayNames = new Map();
22
16
  /** Container for runtime config updates (pi provider dynamic reload). */
23
17
  configContainer;
24
18
  register(kind, factory) {
25
19
  this.factories.set(kind, factory);
26
20
  }
27
- /**
28
- * 注册 agent 的显示名。应在 register(kind, factory) 后调用。
29
- * 用于 agentDisplayName 等全局查询,实现单点真相。
30
- */
31
- registerDisplayName(kind, displayName) {
32
- this.displayNames.set(kind, displayName);
33
- }
34
- /**
35
- * 获取 agent 显示名。未注册时返回原始 kind。
36
- */
37
- getDisplayName(kind) {
38
- return this.displayNames.get(kind) ?? kind;
39
- }
40
- /**
41
- * 设置全局 registry 实例,供 agentDisplayName 等全局函数使用。
42
- * 在 index.ts 初始化所有 agents 后调用。
43
- */
44
- static setGlobalInstance(reg) {
45
- globalRegistry = reg;
46
- }
47
- /**
48
- * 获取全局 registry 实例。
49
- */
50
- static getGlobalInstance() {
51
- return globalRegistry;
52
- }
53
21
  /**
54
22
  * Set config container reference. Call after bridge config is updated.
55
23
  * Factory functions can read latest config from here.
@@ -263,6 +263,22 @@ export interface AssistantEvent {
263
263
  content: AssistantContent[];
264
264
  };
265
265
  }
266
+ /**
267
+ * Authoritative final answer from an agent protocol.
268
+ *
269
+ * This is deliberately separate from ordinary assistant text: progress
270
+ * narration and tool-loop messages have lower display priority than the answer
271
+ * the user actually asked for. Agents without an explicit protocol marker
272
+ * promote their last text block when the turn finishes.
273
+ */
274
+ export interface FinalAnswerEvent {
275
+ type: 'final_answer';
276
+ text: string;
277
+ /** Source item id, used to remove the streamed progress copy of the answer. */
278
+ itemId?: string;
279
+ complete?: boolean;
280
+ timestamp?: string;
281
+ }
266
282
  export interface UserEvent {
267
283
  type: 'user';
268
284
  timestamp?: string;
@@ -470,7 +486,7 @@ export interface TurnDiffEvent {
470
486
  export type RunnerLifetime = 'turn' | 'workspace';
471
487
  /** 工具渲染 hint:runner 自愿声明,渲染层只消费不推断。 */
472
488
  export type ToolHint = 'shell' | 'file' | 'search' | 'web' | 'patch';
473
- export type AgentEvent = SystemInitEvent | SystemCompactEvent | ApprovalRequestedEvent | ApprovalResolvedEvent | ApprovalViewUpdatedEvent | ApprovalExpiredEvent | AssistantEvent | UserEvent | ResultEvent | PlanEvent | FileChangeEvent | TurnStartedEvent | TurnDiffEvent | NoticeEvent | SessionInfoEvent | RunStatusEvent | BackgroundJobsEvent | GoalStateEvent;
489
+ export type AgentEvent = SystemInitEvent | SystemCompactEvent | ApprovalRequestedEvent | ApprovalResolvedEvent | ApprovalViewUpdatedEvent | ApprovalExpiredEvent | AssistantEvent | FinalAnswerEvent | UserEvent | ResultEvent | PlanEvent | FileChangeEvent | TurnStartedEvent | TurnDiffEvent | NoticeEvent | SessionInfoEvent | RunStatusEvent | BackgroundJobsEvent | GoalStateEvent;
474
490
  export interface SpawnOptions {
475
491
  cwd: string;
476
492
  sessionId?: string;
@@ -277,14 +277,16 @@ export class SessionIndex {
277
277
  if (!st.isDirectory())
278
278
  continue;
279
279
  }
280
- catch {
280
+ catch (err) {
281
+ getLogger().debug(`[session-index] skipped unreadable project dir ${subdir}: ${err?.message ?? String(err)}`);
281
282
  continue;
282
283
  }
283
284
  let files;
284
285
  try {
285
286
  files = fs.readdirSync(subdir).filter((f) => f.endsWith('.jsonl'));
286
287
  }
287
- catch {
288
+ catch (err) {
289
+ getLogger().debug(`[session-index] skipped unreadable project subdir ${subdir}: ${err?.message ?? String(err)}`);
288
290
  continue;
289
291
  }
290
292
  for (const f of files) {
@@ -215,8 +215,8 @@ export class KimiSessionReader {
215
215
  state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
216
216
  stateParsed = true;
217
217
  }
218
- catch {
219
- // Corrupt state.json — cannot determine cwd → skip below
218
+ catch (err) {
219
+ getLogger().debug(`[kimi-session-reader] corrupt state.json skipped session=${entry.sessionId}: ${err?.message ?? String(err)}`);
220
220
  }
221
221
  }
222
222
  // Index workDir is v1-only; v2 sessions may have empty/missing workDir.
@@ -510,7 +510,8 @@ export class OpencodeSessionReader {
510
510
  // Check if updated within STALE_MS
511
511
  return !isStale(entry.updated);
512
512
  }
513
- catch {
513
+ catch (err) {
514
+ getLogger().debug(`[opencode-session-reader] isSessionActive failed session=${sessionId}: ${err?.message ?? String(err)}`);
514
515
  return false;
515
516
  }
516
517
  }
@@ -10,6 +10,7 @@ import { encodeProjectDirName, samePath } from '../../platform/path.js';
10
10
  import { paginate } from '../common/pagination.js';
11
11
  import { sortByRecencyDesc } from '../common/recency.js';
12
12
  import { TtlCache } from '../../common/ttl-cache.js';
13
+ import { getLogger } from '../../logger/index.js';
13
14
  function defaultPiDir() {
14
15
  return path.join(os.homedir(), '.pi', 'agent');
15
16
  }
@@ -356,8 +357,8 @@ export class PiSessionReader {
356
357
  mtime: st.mtimeMs,
357
358
  });
358
359
  }
359
- catch {
360
- // skip
360
+ catch (err) {
361
+ getLogger().debug(`[pi-session-reader] skipped ${full}: ${err?.message ?? String(err)}`);
361
362
  }
362
363
  }
363
364
  // Same-mtime ties: secondary key keeps the full order deterministic.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lark-remote",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
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",