lark-remote 0.4.3 → 0.4.5

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 (106) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/binder.d.ts +11 -10
  3. package/dist/binder.js +17 -16
  4. package/dist/bridge/inbound-media.d.ts +1 -27
  5. package/dist/bridge/inbound-media.js +13 -14
  6. package/dist/bridge/index.d.ts +49 -49
  7. package/dist/bridge/index.js +99 -92
  8. package/dist/bridge/queue-manager.d.ts +33 -26
  9. package/dist/bridge/queue-manager.js +117 -117
  10. package/dist/card/bash-renderer.js +4 -4
  11. package/dist/card/card-shared.d.ts +1 -1
  12. package/dist/card/card-shared.js +2 -2
  13. package/dist/card/run-renderer.js +8 -8
  14. package/dist/card/run-state.js +81 -31
  15. package/dist/card/tool-render.js +6 -2
  16. package/dist/cli.js +5 -6
  17. package/dist/clone.d.ts +1 -6
  18. package/dist/clone.js +4 -4
  19. package/dist/common/clamp.d.ts +2 -0
  20. package/dist/common/clamp.js +5 -0
  21. package/dist/common/fs.d.ts +4 -3
  22. package/dist/common/fs.js +5 -4
  23. package/dist/config/dir.d.ts +4 -5
  24. package/dist/config/dir.js +7 -8
  25. package/dist/config/wizard.js +2 -2
  26. package/dist/connector/index.d.ts +0 -2
  27. package/dist/connector/index.js +5 -5
  28. package/dist/inbound/receipt.d.ts +1 -3
  29. package/dist/inbound/receipt.js +2 -2
  30. package/dist/inbound/turn-assembler.d.ts +1 -3
  31. package/dist/inbound/turn-assembler.js +2 -2
  32. package/dist/index.js +30 -22
  33. package/dist/instance-lock.js +4 -4
  34. package/dist/persistence/atomic-write.js +3 -3
  35. package/dist/platform/agent-stopper.d.ts +1 -1
  36. package/dist/platform/terminator-posix.js +5 -5
  37. package/dist/platform/terminator-win32.js +1 -1
  38. package/dist/router/card-helpers.d.ts +1 -3
  39. package/dist/router/card-helpers.js +2 -2
  40. package/dist/router/config/codex.js +1 -1
  41. package/dist/router/config/common/model-patch.d.ts +1 -2
  42. package/dist/router/config/common/model-patch.js +1 -2
  43. package/dist/router/config/opencode.js +1 -1
  44. package/dist/router/config/pi.js +1 -1
  45. package/dist/router/index.d.ts +22 -22
  46. package/dist/router/index.js +71 -52
  47. package/dist/router/order-exec-dispatch.d.ts +3 -10
  48. package/dist/router/order-exec-dispatch.js +3 -3
  49. package/dist/router/{utils.d.ts → usage-format.d.ts} +2 -4
  50. package/dist/router/{utils.js → usage-format.js} +3 -8
  51. package/dist/runner/claude/runner.d.ts +1 -1
  52. package/dist/runner/claude/runner.js +1 -2
  53. package/dist/runner/claude/session.d.ts +2 -4
  54. package/dist/runner/claude/session.js +7 -9
  55. package/dist/runner/common/acp/{protocol-helpers.d.ts → acp-approval.d.ts} +2 -8
  56. package/dist/runner/common/acp/{protocol-helpers.js → acp-approval.js} +4 -3
  57. package/dist/runner/common/acp/base-acp-runner.d.ts +1 -1
  58. package/dist/runner/common/acp/base-acp-runner.js +1 -1
  59. package/dist/runner/common/acp/base-translator.d.ts +43 -0
  60. package/dist/runner/common/acp/base-translator.js +83 -18
  61. package/dist/runner/common/connection-based-runner.d.ts +4 -4
  62. package/dist/runner/common/connection-based-runner.js +3 -3
  63. package/dist/runner/common/jsonrpc/connection-manager.d.ts +12 -12
  64. package/dist/runner/common/jsonrpc/connection-manager.js +36 -36
  65. package/dist/runner/common/{usage.d.ts → result-usage.d.ts} +1 -1
  66. package/dist/runner/common/{usage.js → result-usage.js} +2 -2
  67. package/dist/runner/common/spawning-runner.d.ts +2 -2
  68. package/dist/runner/common/spawning-runner.js +8 -8
  69. package/dist/runner/common/{runner-utils.d.ts → synthetic-events.d.ts} +4 -4
  70. package/dist/runner/common/{runner-utils.js → synthetic-events.js} +5 -5
  71. package/dist/runner/dsh/client.d.ts +1 -1
  72. package/dist/runner/dsh/client.js +1 -1
  73. package/dist/runner/dsh/runner.js +1 -1
  74. package/dist/runner/kimi/acp/runner.d.ts +1 -1
  75. package/dist/runner/kimi/acp/runner.js +1 -1
  76. package/dist/runner/kimi/acp/translator.js +1 -1
  77. package/dist/runner/opencode/acp/runner.d.ts +2 -2
  78. package/dist/runner/opencode/acp/runner.js +1 -1
  79. package/dist/runner/opencode/acp/translator.d.ts +22 -1
  80. package/dist/runner/opencode/acp/translator.js +61 -1
  81. package/dist/runner/pi/rpc/runner.d.ts +1 -1
  82. package/dist/runner/pi/rpc/translator.d.ts +0 -1
  83. package/dist/runner/pi/rpc/translator.js +0 -2
  84. package/dist/runner/registry.d.ts +3 -3
  85. package/dist/runner/registry.js +3 -3
  86. package/dist/session/claude/session-index.d.ts +1 -1
  87. package/dist/session/claude/session-index.js +1 -1
  88. package/dist/session/claude/session-reader.d.ts +1 -1
  89. package/dist/session/claude/session-reader.js +1 -1
  90. package/dist/session/codex/rollout-reader.js +2 -2
  91. package/dist/session/dsh/sessions.d.ts +1 -1
  92. package/dist/session/dsh/sessions.js +1 -1
  93. package/dist/session/kimi/sessions.d.ts +0 -4
  94. package/dist/session/kimi/sessions.js +2 -8
  95. package/dist/session/opencode/sessions.js +3 -3
  96. package/dist/session/registry.d.ts +1 -1
  97. package/dist/session/registry.js +1 -1
  98. package/dist/session/session-store.d.ts +2 -3
  99. package/dist/session/session-store.js +2 -4
  100. package/dist/update/index.d.ts +1 -1
  101. package/dist/update/index.js +1 -1
  102. package/dist/update/version-check.d.ts +5 -3
  103. package/dist/update/version-check.js +10 -8
  104. package/dist/workspace/index.d.ts +4 -4
  105. package/dist/workspace/index.js +5 -5
  106. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Shared helper functions for the ACP protocol layer.
2
+ * ACP approval/permission mapping and lifecycle shared by the kimi and
3
+ * opencode ACP runners.
3
4
  *
4
5
  * Extracted from runner.ts and translator.ts to avoid duplication (G5).
5
6
  * Shape source: approval.ts:28-29 — optionId is opaque, echo back as-is.
@@ -11,7 +12,7 @@ import { getLogger } from '../../../logger/index.js';
11
12
  * ACP option kind values vary between kimi versions; this handles both
12
13
  * the documented names and the observed real names (§2.3).
13
14
  */
14
- export function findOptionIdByKind(options, candidateKinds) {
15
+ function findOptionIdByKind(options, candidateKinds) {
15
16
  for (const kind of candidateKinds) {
16
17
  const found = options.find((opt) => opt.kind === kind);
17
18
  if (found)
@@ -100,4 +101,4 @@ export async function sendAcpSetMode(opts) {
100
101
  getLogger().warn(`[${opts.logTag}] session/set_mode failed (non-fatal): ${err.message}`);
101
102
  }
102
103
  }
103
- //# sourceMappingURL=protocol-helpers.js.map
104
+ //# sourceMappingURL=acp-approval.js.map
@@ -22,7 +22,7 @@ import type { AgentEvent, AgentKind, AgentSessionReader, SpawnOptions } from '..
22
22
  import { ConnectionManager } from '../jsonrpc/connection-manager.js';
23
23
  import { JsonRpcClient } from '../jsonrpc/client.js';
24
24
  import { type SessionNewResult, type SessionResumeResult, type SessionPromptResult, type RequestPermissionParams } from './protocol-types.js';
25
- import { type AcpPendingApproval } from './protocol-helpers.js';
25
+ import { type AcpPendingApproval } from './acp-approval.js';
26
26
  import { ConnectionBasedRunner } from '../connection-based-runner.js';
27
27
  import { BaseAcpTranslator, type AcpTranslatorEvent } from './base-translator.js';
28
28
  export type { AcpTranslatorEvent } from './base-translator.js';
@@ -1,4 +1,4 @@
1
- import { respondAcpApproval, sendAcpSetMode, truncateWithEllipsis, } from './protocol-helpers.js';
1
+ import { respondAcpApproval, sendAcpSetMode, truncateWithEllipsis, } from './acp-approval.js';
2
2
  import { getLogger } from '../../../logger/index.js';
3
3
  import { ConnectionBasedRunner } from '../connection-based-runner.js';
4
4
  export class BaseAcpRunner extends ConnectionBasedRunner {
@@ -15,6 +15,8 @@
15
15
  * handleRequestPermission — approval rendering (abstract)
16
16
  * handleOtherServerRequest — extra reverse RPC (kimi: elicitation)
17
17
  * extractToolResultContent — tool_result content (default: rawOutput)
18
+ * extractToolCallArgsText — streaming-args source (opencode: none)
19
+ * buildToolUseUpdate — tool_use patch from tool_call_update
18
20
  * onUsageSample — extra usage bookkeeping (opencode: cost)
19
21
  */
20
22
  import type { AgentEvent, ApprovalRequestedEvent, ResultEvent } from '../../types.js';
@@ -69,6 +71,13 @@ export declare abstract class BaseAcpTranslator {
69
71
  * approval handler correlate by toolCallId and recover the real command.
70
72
  */
71
73
  private toolCallArgsByCallId;
74
+ /**
75
+ * tool_use name recorded at tool_call time (kind mapping wins, else title).
76
+ * A later tool_call_update may carry only a title (opencode's completed
77
+ * update has neither kind nor rawInput); pairing it with the recorded name
78
+ * lets a tool_use patch render the original title as a subtitle.
79
+ */
80
+ private toolCallNameByCallId;
72
81
  /**
73
82
  * Handle a notification from the ACP server and return translated events.
74
83
  * Wire envelope: {sessionId, update: {sessionUpdate: '<kind>', ...}} — the
@@ -132,8 +141,42 @@ export declare abstract class BaseAcpTranslator {
132
141
  * command (see toolCallArgsByCallId).
133
142
  */
134
143
  protected getTrackedToolCallArgs(toolCallId: string): string | undefined;
144
+ /** tool_use name recorded at tool_call time (kind mapping wins, else title). */
145
+ protected getToolCallName(toolCallId: string): string | undefined;
146
+ /**
147
+ * kind → canonical tool name (信息保真 C3.1). Shared by the initial tool_call
148
+ * handler and the tool_call_update patch builder so both agree on the name.
149
+ */
150
+ protected toolNameFromKind(kind: string | undefined): string | undefined;
151
+ /**
152
+ * Streaming-args text carried by a tool_call/_update notification.
153
+ *
154
+ * Default: the notification's content text — kimi's events-map.ts encodes the
155
+ * accumulated args there (toolCallDeltaToSessionUpdate: content.text =
156
+ * accumulator.args), and the approval handler relies on it.
157
+ *
158
+ * opencode overrides this to return undefined: its tool_call/_update content
159
+ * is the shell OUTPUT snapshot (decompiled acp service `WP`), never args
160
+ * (args ride rawInput in `XC`/`BP`). Treating that output as args would
161
+ * corrupt approval command recovery — the hazard this hook exists to close.
162
+ */
163
+ protected extractToolCallArgsText(event: ToolCallEvent | ToolCallUpdateEvent): string | undefined;
164
+ /**
165
+ * tool_call_update → optional tool_use patch event. When an update carries
166
+ * richer identity than the initial tool_call did (opencode emits the
167
+ * tool_call while args are still streaming, then delivers the real title /
168
+ * rawInput in later updates), the returned `assistant`/`tool_use` block is
169
+ * merged into the existing tool block by the card reducer (matched by id)
170
+ * instead of creating a duplicate panel.
171
+ *
172
+ * Default: null (kimi self-produces its Bash tool_use from terminal/create
173
+ * with the real command, so it needs no patch). opencode overrides.
174
+ */
175
+ protected buildToolUseUpdate(_event: ToolCallUpdateEvent): AgentEvent | null;
135
176
  /** Record latest args text, bounded so a long session cannot grow it forever. */
136
177
  private trackToolCallArgs;
178
+ /** Record tool_use name, bounded to keep a long session from growing it. */
179
+ private trackToolCallName;
137
180
  /**
138
181
  * tool_result content extraction. Default: rawOutput passthrough (kimi
139
182
  * semantics). opencode overrides — its rawOutput is an OBJECT
@@ -65,6 +65,13 @@ export class BaseAcpTranslator {
65
65
  * approval handler correlate by toolCallId and recover the real command.
66
66
  */
67
67
  toolCallArgsByCallId = new Map();
68
+ /**
69
+ * tool_use name recorded at tool_call time (kind mapping wins, else title).
70
+ * A later tool_call_update may carry only a title (opencode's completed
71
+ * update has neither kind nor rawInput); pairing it with the recorded name
72
+ * lets a tool_use patch render the original title as a subtitle.
73
+ */
74
+ toolCallNameByCallId = new Map();
68
75
  /**
69
76
  * Handle a notification from the ACP server and return translated events.
70
77
  * Wire envelope: {sessionId, update: {sessionUpdate: '<kind>', ...}} — the
@@ -231,15 +238,16 @@ export class BaseAcpTranslator {
231
238
  // Keep raw string as-is
232
239
  }
233
240
  }
234
- const argsText = extractToolCallContentText(event.content);
241
+ const argsText = this.extractToolCallArgsText(event);
235
242
  if (argsText !== undefined) {
236
243
  this.trackToolCallArgs(event.toolCallId, argsText);
237
244
  }
238
245
  else if (event.rawInput !== undefined && event.rawInput !== null) {
239
246
  this.trackToolCallArgs(event.toolCallId, stringifyUnknown(event.rawInput));
240
247
  }
241
- const mapped = event.kind ? KIND_TO_TOOL[event.kind] : undefined;
248
+ const mapped = this.toolNameFromKind(event.kind);
242
249
  const name = mapped ?? event.title;
250
+ this.trackToolCallName(event.toolCallId, name);
243
251
  const summary = mapped && event.title && event.title !== name ? event.title : undefined;
244
252
  return [
245
253
  {
@@ -265,6 +273,11 @@ export class BaseAcpTranslator {
265
273
  handleToolCallUpdate(event, _sessionId) {
266
274
  const isError = event.status === 'failed';
267
275
  const content = this.extractToolResultContent(event);
276
+ // Patch the existing tool block with identity that was not available at the
277
+ // initial tool_call (opencode streams args after emitting tool_call). The
278
+ // reducer merges by id, so this never duplicates the panel. Built before
279
+ // the terminal cleanup below so it can still resolve the recorded name.
280
+ const useUpdate = this.buildToolUseUpdate(event);
268
281
  // Terminal updates end the args-streaming phase — drop the tracked entry.
269
282
  // In-progress delta text is cumulative (events-map.ts
270
283
  // toolCallDeltaToSessionUpdate: content.text = accumulator.args), so the
@@ -273,7 +286,7 @@ export class BaseAcpTranslator {
273
286
  this.toolCallArgsByCallId.delete(event.toolCallId);
274
287
  }
275
288
  else {
276
- const argsText = extractToolCallContentText(event.content);
289
+ const argsText = this.extractToolCallArgsText(event);
277
290
  if (argsText !== undefined) {
278
291
  this.trackToolCallArgs(event.toolCallId, argsText);
279
292
  }
@@ -281,22 +294,24 @@ export class BaseAcpTranslator {
281
294
  this.trackToolCallArgs(event.toolCallId, stringifyUnknown(event.rawInput));
282
295
  }
283
296
  }
284
- return [
285
- {
286
- type: 'user',
287
- message: {
288
- content: [
289
- {
290
- type: 'tool_result',
291
- tool_use_id: event.toolCallId,
292
- content,
293
- is_error: isError,
294
- },
295
- ],
296
- },
297
- timestamp: new Date().toISOString(),
297
+ const events = [];
298
+ if (useUpdate)
299
+ events.push(useUpdate);
300
+ events.push({
301
+ type: 'user',
302
+ message: {
303
+ content: [
304
+ {
305
+ type: 'tool_result',
306
+ tool_use_id: event.toolCallId,
307
+ content,
308
+ is_error: isError,
309
+ },
310
+ ],
298
311
  },
299
- ];
312
+ timestamp: new Date().toISOString(),
313
+ });
314
+ return events;
300
315
  }
301
316
  /**
302
317
  * Latest tracked streaming-args text for a tool call (undefined when never
@@ -307,6 +322,46 @@ export class BaseAcpTranslator {
307
322
  getTrackedToolCallArgs(toolCallId) {
308
323
  return this.toolCallArgsByCallId.get(toolCallId);
309
324
  }
325
+ /** tool_use name recorded at tool_call time (kind mapping wins, else title). */
326
+ getToolCallName(toolCallId) {
327
+ return this.toolCallNameByCallId.get(toolCallId);
328
+ }
329
+ /**
330
+ * kind → canonical tool name (信息保真 C3.1). Shared by the initial tool_call
331
+ * handler and the tool_call_update patch builder so both agree on the name.
332
+ */
333
+ toolNameFromKind(kind) {
334
+ return kind ? KIND_TO_TOOL[kind] : undefined;
335
+ }
336
+ /**
337
+ * Streaming-args text carried by a tool_call/_update notification.
338
+ *
339
+ * Default: the notification's content text — kimi's events-map.ts encodes the
340
+ * accumulated args there (toolCallDeltaToSessionUpdate: content.text =
341
+ * accumulator.args), and the approval handler relies on it.
342
+ *
343
+ * opencode overrides this to return undefined: its tool_call/_update content
344
+ * is the shell OUTPUT snapshot (decompiled acp service `WP`), never args
345
+ * (args ride rawInput in `XC`/`BP`). Treating that output as args would
346
+ * corrupt approval command recovery — the hazard this hook exists to close.
347
+ */
348
+ extractToolCallArgsText(event) {
349
+ return extractToolCallContentText(event.content);
350
+ }
351
+ /**
352
+ * tool_call_update → optional tool_use patch event. When an update carries
353
+ * richer identity than the initial tool_call did (opencode emits the
354
+ * tool_call while args are still streaming, then delivers the real title /
355
+ * rawInput in later updates), the returned `assistant`/`tool_use` block is
356
+ * merged into the existing tool block by the card reducer (matched by id)
357
+ * instead of creating a duplicate panel.
358
+ *
359
+ * Default: null (kimi self-produces its Bash tool_use from terminal/create
360
+ * with the real command, so it needs no patch). opencode overrides.
361
+ */
362
+ buildToolUseUpdate(_event) {
363
+ return null;
364
+ }
310
365
  /** Record latest args text, bounded so a long session cannot grow it forever. */
311
366
  trackToolCallArgs(toolCallId, argsText) {
312
367
  this.toolCallArgsByCallId.delete(toolCallId);
@@ -317,6 +372,16 @@ export class BaseAcpTranslator {
317
372
  this.toolCallArgsByCallId.delete(oldest);
318
373
  }
319
374
  }
375
+ /** Record tool_use name, bounded to keep a long session from growing it. */
376
+ trackToolCallName(toolCallId, name) {
377
+ this.toolCallNameByCallId.delete(toolCallId);
378
+ this.toolCallNameByCallId.set(toolCallId, name);
379
+ if (this.toolCallNameByCallId.size > 100) {
380
+ const oldest = this.toolCallNameByCallId.keys().next().value;
381
+ if (oldest !== undefined)
382
+ this.toolCallNameByCallId.delete(oldest);
383
+ }
384
+ }
320
385
  /**
321
386
  * tool_result content extraction. Default: rawOutput passthrough (kimi
322
387
  * semantics). opencode overrides — its rawOutput is an OBJECT
@@ -60,9 +60,9 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
60
60
  * 两件事,顺序不能反:
61
61
  * 1. 若本连接就是当前在途 turn 的连接,先发协议取消(codex turn/interrupt /
62
62
  * ACP session/cancel / pi abort)。**归属判定不能省**:runner 实例按 agent
63
- * 维度共享(一个实例管多条 workspace 连接),无条件调用
63
+ * 维度共享(一个实例管多条 cwd 连接),无条件调用
64
64
  * `cancelCurrentTurn()` 会用它自己的 currentClient/activeSessionId 去打断
65
- * **另一个 workspace** 正在跑的 turn;
65
+ * **另一个 cwd** 正在跑的 turn;
66
66
  * 2. 关 stdin。stdio 型 agent server 在 stdin EOF 后自行退出——只发协议取消
67
67
  * 不会让进程退出,优雅段会空转到 grace 超时后照样树杀,等于通道白注册。
68
68
  */
@@ -118,9 +118,9 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
118
118
  protected respawnAfterConnectionLost(opts: SpawnOptions): Promise<void>;
119
119
  /** Hook: clear subclass turn state in run() finally (session ids, translator, flags). */
120
120
  protected abstract clearTurnState(): void;
121
- /** Release the connection for a workspace (subclass dispatches to its manager). */
121
+ /** Release the connection for a cwd (subclass dispatches to its manager). */
122
122
  protected abstract releaseConnection(cwd: string): Promise<void>;
123
- /** Notify the connection manager that the workspace is idle (subclass). */
123
+ /** Notify the connection manager that the cwd is idle (subclass). */
124
124
  protected abstract notifyIdle(cwd: string): void;
125
125
  /** Dispose all connections (subclass dispatches to its manager). */
126
126
  protected abstract disposeConnections(): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { ConnectionLostError } from './jsonrpc/client.js';
2
2
  import { getLogger } from '../../logger/index.js';
3
- import { syntheticInitEvent } from './runner-utils.js';
3
+ import { syntheticInitEvent } from './synthetic-events.js';
4
4
  import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
5
5
  /** How long to wait for turn output notifications before failing.
6
6
  * 30 分钟(单源于 config 的 DEFAULT_TURN_IDLE_TIMEOUT_MINUTES):ACP/app-server
@@ -37,9 +37,9 @@ export class ConnectionBasedRunner {
37
37
  * 两件事,顺序不能反:
38
38
  * 1. 若本连接就是当前在途 turn 的连接,先发协议取消(codex turn/interrupt /
39
39
  * ACP session/cancel / pi abort)。**归属判定不能省**:runner 实例按 agent
40
- * 维度共享(一个实例管多条 workspace 连接),无条件调用
40
+ * 维度共享(一个实例管多条 cwd 连接),无条件调用
41
41
  * `cancelCurrentTurn()` 会用它自己的 currentClient/activeSessionId 去打断
42
- * **另一个 workspace** 正在跑的 turn;
42
+ * **另一个 cwd** 正在跑的 turn;
43
43
  * 2. 关 stdin。stdio 型 agent server 在 stdin EOF 后自行退出——只发协议取消
44
44
  * 不会让进程退出,优雅段会空转到 grace 超时后照样树杀,等于通道白注册。
45
45
  */
@@ -1,9 +1,9 @@
1
1
  /**
2
- * ConnectionManager: manages agent-server connections per workspace, shared by
2
+ * ConnectionManager: manages agent-server connections per cwd, shared by
3
3
  * all workspace-lifetime JSON-line runners (codex app-server, kimi acp,
4
4
  * opencode acp, and pi rpc).
5
5
  *
6
- * Each workspace gets its own connection (transport + client). Connections are
6
+ * Each cwd gets its own connection (transport + client). Connections are
7
7
  * created on demand via `acquire()`, cached for reuse, and released after an
8
8
  * idle timeout (30 minutes default).
9
9
  *
@@ -92,7 +92,7 @@ export declare class ConnectionManager<TClient extends ConnectionClient = JsonRp
92
92
  private readonly logTag;
93
93
  private readonly stoppers;
94
94
  /** Callback when a connection is lost — cleared from slot map. */
95
- onConnectionLost?: (workspace: string) => void;
95
+ onConnectionLost?: (cwd: string) => void;
96
96
  /**
97
97
  * 协议停止通道工厂(design §3.3):连接建立后按 pid 登记到 AgentStopperRegistry,
98
98
  * 供 Terminator 在**优雅**停止时回调(win32 上没有可拦截的 SIGTERM,只能靠
@@ -108,37 +108,37 @@ export declare class ConnectionManager<TClient extends ConnectionClient = JsonRp
108
108
  }) => AgentStopper | undefined;
109
109
  constructor(opts: ConnectionManagerOptions<TClient>);
110
110
  /**
111
- * Acquire a connection for the given workspace.
111
+ * Acquire a connection for the given cwd.
112
112
  * Creates a new connection if one does not exist or the previous one was lost
113
113
  * (or, for session-bound protocols, bound to a different session).
114
114
  * Serializes creation so concurrent calls share the same connection.
115
115
  */
116
- acquire(workspace: string, req?: AcquireRequest): Promise<TClient>;
116
+ acquire(cwd: string, req?: AcquireRequest): Promise<TClient>;
117
117
  /**
118
118
  * Record the session id a fresh connection actually bound to (e.g. discovered
119
119
  * via a protocol `get_state` after a fresh spawn). This lets a subsequent run
120
120
  * resume the SAME session on the live connection instead of respawning it.
121
121
  */
122
- bindSession(workspace: string, sessionId: string): void;
122
+ bindSession(cwd: string, sessionId: string): void;
123
123
  /**
124
- * Release (dispose) a connection for the given workspace.
124
+ * Release (dispose) a connection for the given cwd.
125
125
  */
126
- release(workspace: string): Promise<void>;
126
+ release(cwd: string): Promise<void>;
127
127
  /**
128
128
  * Dispose all connections.
129
129
  */
130
130
  disposeAll(): Promise<void>;
131
131
  /**
132
- * Notify that the workspace is active — disarm the idle timer.
132
+ * Notify that the cwd is active — disarm the idle timer.
133
133
  */
134
- notifyActivity(workspace: string): void;
134
+ notifyActivity(cwd: string): void;
135
135
  /**
136
- * Notify that the workspace is idle — arm the idle timer.
136
+ * Notify that the cwd is idle — arm the idle timer.
137
137
  *
138
138
  * `idleTtlMs: 0` = 不回收(与 claude 会话层的 `armIdleTimer` 同口径;按 0
139
139
  * setTimeout 会秒删刚建好的连接,等于每条消息重起一次进程)。
140
140
  */
141
- notifyIdle(workspace: string): void;
141
+ notifyIdle(cwd: string): void;
142
142
  private createClient;
143
143
  private clearIdleTimer;
144
144
  private disposeSlot;
@@ -1,9 +1,9 @@
1
1
  /**
2
- * ConnectionManager: manages agent-server connections per workspace, shared by
2
+ * ConnectionManager: manages agent-server connections per cwd, shared by
3
3
  * all workspace-lifetime JSON-line runners (codex app-server, kimi acp,
4
4
  * opencode acp, and pi rpc).
5
5
  *
6
- * Each workspace gets its own connection (transport + client). Connections are
6
+ * Each cwd gets its own connection (transport + client). Connections are
7
7
  * created on demand via `acquire()`, cached for reuse, and released after an
8
8
  * idle timeout (30 minutes default).
9
9
  *
@@ -62,13 +62,13 @@ export class ConnectionManager {
62
62
  this.stoppers = opts.stoppers ?? agentStopperRegistry;
63
63
  }
64
64
  /**
65
- * Acquire a connection for the given workspace.
65
+ * Acquire a connection for the given cwd.
66
66
  * Creates a new connection if one does not exist or the previous one was lost
67
67
  * (or, for session-bound protocols, bound to a different session).
68
68
  * Serializes creation so concurrent calls share the same connection.
69
69
  */
70
- async acquire(workspace, req = {}) {
71
- const existing = this.slots.get(workspace);
70
+ async acquire(cwd, req = {}) {
71
+ const existing = this.slots.get(cwd);
72
72
  if (existing) {
73
73
  // If a create is in flight, wait for it
74
74
  if (existing.createPromise) {
@@ -85,10 +85,10 @@ export class ConnectionManager {
85
85
  })) {
86
86
  return existing.client;
87
87
  }
88
- this.disposeSlot(workspace);
88
+ this.disposeSlot(cwd);
89
89
  }
90
- const createPromise = this.createClient(workspace, req);
91
- this.slots.set(workspace, {
90
+ const createPromise = this.createClient(cwd, req);
91
+ this.slots.set(cwd, {
92
92
  client: null, // placeholder
93
93
  boundSessionId: req.sessionId,
94
94
  idleTimer: null,
@@ -99,11 +99,11 @@ export class ConnectionManager {
99
99
  // 竞态:创建期间 release()/disposeAll() 已把 slot 删掉(并可能已
100
100
  // dispose 连接)。此时不能再把新连接塞回 slot——否则会复活一个无人管理、
101
101
  // 无 idle timer 的连接泄漏。归还给调用者继续使用,但不再缓存。
102
- const slotAfterCreate = this.slots.get(workspace);
102
+ const slotAfterCreate = this.slots.get(cwd);
103
103
  if (!slotAfterCreate || slotAfterCreate.createPromise !== createPromise) {
104
104
  return client;
105
105
  }
106
- this.slots.set(workspace, {
106
+ this.slots.set(cwd, {
107
107
  client,
108
108
  boundSessionId: req.sessionId,
109
109
  idleTimer: null,
@@ -114,9 +114,9 @@ export class ConnectionManager {
114
114
  catch (err) {
115
115
  // 只删除自己的槽位:并发 release + 重新 acquire 后 slot 可能已被新的
116
116
  // createPromise 占据,误删会破坏新连接。
117
- const current = this.slots.get(workspace);
117
+ const current = this.slots.get(cwd);
118
118
  if (current && current.createPromise === createPromise) {
119
- this.slots.delete(workspace);
119
+ this.slots.delete(cwd);
120
120
  }
121
121
  throw err;
122
122
  }
@@ -126,16 +126,16 @@ export class ConnectionManager {
126
126
  * via a protocol `get_state` after a fresh spawn). This lets a subsequent run
127
127
  * resume the SAME session on the live connection instead of respawning it.
128
128
  */
129
- bindSession(workspace, sessionId) {
130
- const slot = this.slots.get(workspace);
129
+ bindSession(cwd, sessionId) {
130
+ const slot = this.slots.get(cwd);
131
131
  if (slot)
132
132
  slot.boundSessionId = sessionId;
133
133
  }
134
134
  /**
135
- * Release (dispose) a connection for the given workspace.
135
+ * Release (dispose) a connection for the given cwd.
136
136
  */
137
- async release(workspace) {
138
- this.disposeSlot(workspace);
137
+ async release(cwd) {
138
+ this.disposeSlot(cwd);
139
139
  }
140
140
  /**
141
141
  * Dispose all connections.
@@ -145,46 +145,46 @@ export class ConnectionManager {
145
145
  await Promise.all(workspaces.map((w) => this.disposeSlot(w)));
146
146
  }
147
147
  /**
148
- * Notify that the workspace is active — disarm the idle timer.
148
+ * Notify that the cwd is active — disarm the idle timer.
149
149
  */
150
- notifyActivity(workspace) {
151
- const slot = this.slots.get(workspace);
150
+ notifyActivity(cwd) {
151
+ const slot = this.slots.get(cwd);
152
152
  if (slot) {
153
153
  this.clearIdleTimer(slot);
154
154
  }
155
155
  }
156
156
  /**
157
- * Notify that the workspace is idle — arm the idle timer.
157
+ * Notify that the cwd is idle — arm the idle timer.
158
158
  *
159
159
  * `idleTtlMs: 0` = 不回收(与 claude 会话层的 `armIdleTimer` 同口径;按 0
160
160
  * setTimeout 会秒删刚建好的连接,等于每条消息重起一次进程)。
161
161
  */
162
- notifyIdle(workspace) {
162
+ notifyIdle(cwd) {
163
163
  if (this.idleTtlMs <= 0)
164
164
  return;
165
- const slot = this.slots.get(workspace);
165
+ const slot = this.slots.get(cwd);
166
166
  if (slot) {
167
167
  this.clearIdleTimer(slot);
168
168
  slot.idleTimer = setTimeout(() => {
169
- getLogger().info(`[${this.logTag}] idle timeout for workspace=${workspace}, releasing connection`);
170
- this.disposeSlot(workspace);
169
+ getLogger().info(`[${this.logTag}] idle timeout for cwd=${cwd}, releasing connection`);
170
+ this.disposeSlot(cwd);
171
171
  }, this.idleTtlMs);
172
172
  }
173
173
  }
174
174
  // =========================================================================
175
175
  // Internal
176
176
  // =========================================================================
177
- async createClient(workspace, req) {
177
+ async createClient(cwd, req) {
178
178
  let client = null;
179
179
  const transport = new JsonlRpcTransport({
180
180
  binary: this.binary,
181
181
  args: this.buildArgs(req),
182
- cwd: workspace,
182
+ cwd: cwd,
183
183
  env: this.env,
184
184
  stoppers: this.stoppers,
185
185
  // 通道工厂要拿到**连接本身**:协议取消(codex turn/interrupt、ACP
186
186
  // session/cancel)必须发在这条连接上,不能借 runner 的共享状态——那会
187
- // 打断另一 workspace 正在跑的 turn。工厂是在 transport.start() 内侧调用
187
+ // 打断另一 cwd 正在跑的 turn。工厂是在 transport.start() 内侧调用
188
188
  // 的,那时 client 已由下面的 clientFactory 赋好。
189
189
  stopper: this.stopper
190
190
  ? (pid) => this.stopper?.({ pid, client: client })
@@ -194,12 +194,12 @@ export class ConnectionManager {
194
194
  transport,
195
195
  requestTimeoutMs: this.requestTimeoutMs,
196
196
  baseOnClose: () => {
197
- getLogger().info(`[${this.logTag}] connection closed workspace=${workspace}`);
197
+ getLogger().info(`[${this.logTag}] connection closed cwd=${cwd}`);
198
198
  // 只清理自己创建的 slot:并发 release + 重新 acquire 后 slot 可能已被
199
199
  // 新连接占据,误删会破坏新连接。
200
- if (this.slots.get(workspace)?.client === client) {
201
- this.slots.delete(workspace);
202
- this.onConnectionLost?.(workspace);
200
+ if (this.slots.get(cwd)?.client === client) {
201
+ this.slots.delete(cwd);
202
+ this.onConnectionLost?.(cwd);
203
203
  }
204
204
  },
205
205
  });
@@ -212,18 +212,18 @@ export class ConnectionManager {
212
212
  slot.idleTimer = null;
213
213
  }
214
214
  }
215
- async disposeSlot(workspace) {
216
- const slot = this.slots.get(workspace);
215
+ async disposeSlot(cwd) {
216
+ const slot = this.slots.get(cwd);
217
217
  if (!slot)
218
218
  return;
219
- this.slots.delete(workspace);
219
+ this.slots.delete(cwd);
220
220
  this.clearIdleTimer(slot);
221
221
  if (slot.client) {
222
222
  try {
223
223
  await slot.client.dispose();
224
224
  }
225
225
  catch (err) {
226
- getLogger().warn(`[${this.logTag}] dispose error workspace=${workspace}: ${err}`);
226
+ getLogger().warn(`[${this.logTag}] dispose error cwd=${cwd}: ${err}`);
227
227
  }
228
228
  }
229
229
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Normalized usage extracted from a result event.
3
3
  *
4
- * @module runner/common/usage
4
+ * @module runner/common/result-usage
5
5
  */
6
6
  import type { TokenUsage } from '../types.js';
7
7
  /** Normalized, camelCase view of a `TokenUsage` payload.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Normalized usage extracted from a result event.
3
3
  *
4
- * @module runner/common/usage
4
+ * @module runner/common/result-usage
5
5
  */
6
6
  // ---------------------------------------------------------------------------
7
7
  // Implementation
@@ -30,4 +30,4 @@ export function normalizeResultUsage(usage) {
30
30
  contextLength,
31
31
  };
32
32
  }
33
- //# sourceMappingURL=usage.js.map
33
+ //# sourceMappingURL=result-usage.js.map
@@ -86,7 +86,7 @@ export declare abstract class SpawningRunner {
86
86
  private stopperRegistration;
87
87
  constructor(opts: {
88
88
  pidDir?: string;
89
- workspace: string;
89
+ cwd: string;
90
90
  stopGraceMs?: number;
91
91
  spawnHeartbeatMs?: number;
92
92
  /**
@@ -127,7 +127,7 @@ export declare abstract class SpawningRunner {
127
127
  * finite timeout (review P2-11) against the 'error' event: Node guarantees
128
128
  * 'error' for ENOENT/EACCES, but some binaries fail silently without ever
129
129
  * emitting it. Without the race, the spawn lead-in hangs forever → the
130
- * workspace serial queue never settles → permanent deadlock, and /stop
130
+ * cwd serial queue never settles → permanent deadlock, and /stop
131
131
  * can't recover (Terminator.stop returns early when pid === undefined).
132
132
  * The timeout keeps the deadlock bounded.
133
133
  */