lark-remote 0.1.6 → 0.1.7

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 (188) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.en.md +9 -1
  3. package/README.md +14 -2
  4. package/dist/bridge/approval-coordinator.d.ts +57 -34
  5. package/dist/bridge/approval-coordinator.js +191 -40
  6. package/dist/bridge/inbound-media.d.ts +62 -0
  7. package/dist/bridge/inbound-media.js +282 -0
  8. package/dist/bridge/index.d.ts +78 -7
  9. package/dist/bridge/index.js +202 -79
  10. package/dist/bridge/queue-manager.d.ts +2 -1
  11. package/dist/bridge/queue-manager.js +1 -0
  12. package/dist/card/approval-render.js +152 -36
  13. package/dist/card/bash-card-session.d.ts +2 -24
  14. package/dist/card/bash-card-session.js +6 -73
  15. package/dist/card/bash-renderer.js +2 -3
  16. package/dist/card/card-budget.js +29 -75
  17. package/dist/card/card-session.d.ts +48 -0
  18. package/dist/card/card-session.js +86 -0
  19. package/dist/card/card-shared.d.ts +1 -12
  20. package/dist/card/card-shared.js +1 -26
  21. package/dist/card/run-card-session.d.ts +2 -55
  22. package/dist/card/run-card-session.js +6 -104
  23. package/dist/card/run-renderer.js +99 -232
  24. package/dist/card/run-state.d.ts +13 -0
  25. package/dist/card/run-state.js +25 -16
  26. package/dist/card/text-truncate.d.ts +13 -12
  27. package/dist/card/text-truncate.js +13 -63
  28. package/dist/common/fs.d.ts +8 -0
  29. package/dist/common/fs.js +17 -0
  30. package/dist/common/guards.d.ts +0 -20
  31. package/dist/common/guards.js +0 -43
  32. package/dist/common/sleep.d.ts +2 -0
  33. package/dist/common/sleep.js +5 -0
  34. package/dist/common/truncate.d.ts +39 -0
  35. package/dist/common/truncate.js +92 -0
  36. package/dist/config/codex-config.d.ts +1 -1
  37. package/dist/config/codex-config.js +1 -1
  38. package/dist/config/index.d.ts +88 -23
  39. package/dist/config/index.js +105 -34
  40. package/dist/connector/index.d.ts +69 -2
  41. package/dist/connector/index.js +107 -1
  42. package/dist/index.js +123 -105
  43. package/dist/instance-lock.js +4 -12
  44. package/dist/order/alias-resolve.d.ts +13 -0
  45. package/dist/order/alias-resolve.js +22 -0
  46. package/dist/order/alias-store.d.ts +38 -0
  47. package/dist/order/alias-store.js +106 -0
  48. package/dist/persistence/atomic-write.d.ts +8 -2
  49. package/dist/persistence/atomic-write.js +23 -7
  50. package/dist/restart.js +2 -1
  51. package/dist/router/card-helpers.d.ts +63 -0
  52. package/dist/router/card-helpers.js +146 -0
  53. package/dist/router/config/claude.js +25 -1
  54. package/dist/router/config/codex.js +29 -50
  55. package/dist/router/config/common/model-patch.d.ts +22 -0
  56. package/dist/router/config/common/model-patch.js +26 -0
  57. package/dist/router/config/common/render.js +5 -4
  58. package/dist/router/config/index.d.ts +12 -0
  59. package/dist/router/config/index.js +25 -1
  60. package/dist/router/config/kimi.d.ts +4 -1
  61. package/dist/router/config/kimi.js +27 -0
  62. package/dist/router/config/opencode.js +18 -5
  63. package/dist/router/config/pi.js +4 -5
  64. package/dist/router/config/types.d.ts +11 -2
  65. package/dist/router/index.d.ts +56 -21
  66. package/dist/router/index.js +373 -548
  67. package/dist/router/utils.d.ts +2 -0
  68. package/dist/router/utils.js +4 -0
  69. package/dist/runner/agent-choices-common.d.ts +16 -0
  70. package/dist/runner/agent-choices-common.js +31 -0
  71. package/dist/runner/claude/runner.d.ts +47 -10
  72. package/dist/runner/claude/runner.js +117 -46
  73. package/dist/runner/claude/session.d.ts +166 -0
  74. package/dist/runner/claude/session.js +749 -0
  75. package/dist/runner/codex/app-server/protocol-types.d.ts +31 -39
  76. package/dist/runner/codex/app-server/protocol-types.js +1 -12
  77. package/dist/runner/codex/app-server/runner.d.ts +42 -59
  78. package/dist/runner/codex/app-server/runner.js +160 -337
  79. package/dist/runner/codex/app-server/translator.d.ts +1 -21
  80. package/dist/runner/codex/app-server/translator.js +0 -18
  81. package/dist/runner/codex/index.d.ts +0 -2
  82. package/dist/runner/codex/index.js +0 -2
  83. package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
  84. package/dist/runner/common/acp/protocol-helpers.js +21 -0
  85. package/dist/runner/common/acp/protocol-types.d.ts +238 -0
  86. package/dist/runner/common/acp/protocol-types.js +75 -0
  87. package/dist/runner/common/connection-based-runner.d.ts +111 -0
  88. package/dist/runner/common/connection-based-runner.js +261 -0
  89. package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
  90. package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
  91. package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
  92. package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
  93. package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
  94. package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
  95. package/dist/runner/common/jsonrpc/types.d.ts +32 -0
  96. package/dist/runner/common/jsonrpc/types.js +9 -0
  97. package/dist/runner/common/process-stopper.d.ts +2 -1
  98. package/dist/runner/common/process-stopper.js +1 -0
  99. package/dist/runner/common/runner-utils.d.ts +6 -13
  100. package/dist/runner/common/runner-utils.js +6 -47
  101. package/dist/runner/common/spawning-runner.d.ts +24 -70
  102. package/dist/runner/common/spawning-runner.js +98 -152
  103. package/dist/runner/index.d.ts +4 -4
  104. package/dist/runner/index.js +4 -4
  105. package/dist/runner/kimi/acp/runner.d.ts +106 -0
  106. package/dist/runner/kimi/acp/runner.js +448 -0
  107. package/dist/runner/kimi/acp/translator.d.ts +124 -0
  108. package/dist/runner/kimi/acp/translator.js +336 -0
  109. package/dist/runner/kimi/index.d.ts +1 -1
  110. package/dist/runner/kimi/index.js +1 -1
  111. package/dist/runner/opencode/acp/index.d.ts +1 -0
  112. package/dist/runner/opencode/acp/index.js +2 -0
  113. package/dist/runner/opencode/acp/runner.d.ts +121 -0
  114. package/dist/runner/opencode/acp/runner.js +429 -0
  115. package/dist/runner/opencode/acp/translator.d.ts +119 -0
  116. package/dist/runner/opencode/acp/translator.js +345 -0
  117. package/dist/runner/opencode/index.d.ts +1 -1
  118. package/dist/runner/opencode/index.js +1 -1
  119. package/dist/runner/pi/index.d.ts +1 -1
  120. package/dist/runner/pi/index.js +1 -1
  121. package/dist/runner/pi/rpc/client.d.ts +52 -0
  122. package/dist/runner/pi/rpc/client.js +114 -0
  123. package/dist/runner/pi/rpc/index.d.ts +4 -0
  124. package/dist/runner/pi/rpc/index.js +4 -0
  125. package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
  126. package/dist/runner/pi/rpc/protocol-types.js +21 -0
  127. package/dist/runner/pi/rpc/runner.d.ts +80 -0
  128. package/dist/runner/pi/rpc/runner.js +199 -0
  129. package/dist/runner/pi/rpc/translator.d.ts +55 -0
  130. package/dist/runner/pi/rpc/translator.js +258 -0
  131. package/dist/runner/registry.d.ts +7 -8
  132. package/dist/runner/registry.js +3 -4
  133. package/dist/runner/resolve-agent-choices.js +18 -59
  134. package/dist/runner/sync-agent-choices.js +16 -67
  135. package/dist/runner/types.d.ts +43 -13
  136. package/dist/session/claude/session-index.js +2 -3
  137. package/dist/session/claude/session-reader.js +3 -6
  138. package/dist/session/claude/sessions.js +6 -13
  139. package/dist/session/codex/rollout-reader.d.ts +2 -2
  140. package/dist/session/codex/rollout-reader.js +14 -17
  141. package/dist/session/codex/sessions.d.ts +1 -1
  142. package/dist/session/codex/sessions.js +2 -2
  143. package/dist/session/common/content-blocks.d.ts +7 -0
  144. package/dist/session/common/content-blocks.js +13 -9
  145. package/dist/session/common/jsonl.d.ts +8 -25
  146. package/dist/session/common/jsonl.js +126 -261
  147. package/dist/session/common/pagination.d.ts +28 -0
  148. package/dist/session/common/pagination.js +33 -0
  149. package/dist/session/kimi/sessions.d.ts +34 -1
  150. package/dist/session/kimi/sessions.js +208 -98
  151. package/dist/session/opencode/sessions.d.ts +17 -3
  152. package/dist/session/opencode/sessions.js +29 -29
  153. package/dist/session/pi/sessions.d.ts +5 -1
  154. package/dist/session/pi/sessions.js +12 -16
  155. package/dist/session/session-store.d.ts +11 -1
  156. package/dist/session/session-store.js +75 -178
  157. package/dist/update/index.d.ts +2 -2
  158. package/dist/update/index.js +2 -2
  159. package/dist/update/install.d.ts +20 -2
  160. package/dist/update/install.js +51 -7
  161. package/package.json +1 -1
  162. package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
  163. package/dist/runner/codex/argv.d.ts +0 -33
  164. package/dist/runner/codex/argv.js +0 -40
  165. package/dist/runner/codex/factory.d.ts +0 -29
  166. package/dist/runner/codex/factory.js +0 -39
  167. package/dist/runner/codex/jsonl.d.ts +0 -37
  168. package/dist/runner/codex/jsonl.js +0 -221
  169. package/dist/runner/codex/runner.d.ts +0 -66
  170. package/dist/runner/codex/runner.js +0 -129
  171. package/dist/runner/common/exec-translator.d.ts +0 -73
  172. package/dist/runner/common/exec-translator.js +0 -105
  173. package/dist/runner/kimi/jsonl.d.ts +0 -28
  174. package/dist/runner/kimi/jsonl.js +0 -143
  175. package/dist/runner/kimi/runner.d.ts +0 -49
  176. package/dist/runner/kimi/runner.js +0 -105
  177. package/dist/runner/opencode/argv.d.ts +0 -32
  178. package/dist/runner/opencode/argv.js +0 -35
  179. package/dist/runner/opencode/jsonl.d.ts +0 -68
  180. package/dist/runner/opencode/jsonl.js +0 -295
  181. package/dist/runner/opencode/runner.d.ts +0 -61
  182. package/dist/runner/opencode/runner.js +0 -93
  183. package/dist/runner/pi/jsonl.d.ts +0 -128
  184. package/dist/runner/pi/jsonl.js +0 -249
  185. package/dist/runner/pi/runner.d.ts +0 -36
  186. package/dist/runner/pi/runner.js +0 -76
  187. package/dist/session/common/catch-up.d.ts +0 -12
  188. package/dist/session/common/catch-up.js +0 -31
@@ -12,6 +12,8 @@ export interface SessionDisplayUsage {
12
12
  cacheCreationTokens?: number;
13
13
  }
14
14
  export declare function activeRunUsage(activeRun: ActiveRunSnapshot | undefined): SessionDisplayUsage | undefined;
15
+ /** Clamp `v` into [min, max]. */
16
+ export declare function clampInt(v: number, min: number, max: number): number;
15
17
  /**
16
18
  * Format usage stats (context length, compact count, cache tokens) into a display string.
17
19
  * Each stat on its own line with blank lines between sections.
@@ -13,6 +13,10 @@ export function activeRunUsage(activeRun) {
13
13
  };
14
14
  }
15
15
  // --- Extracted helpers for refactoring ---
16
+ /** Clamp `v` into [min, max]. */
17
+ export function clampInt(v, min, max) {
18
+ return Math.min(Math.max(v, min), max);
19
+ }
16
20
  /**
17
21
  * Format number to human-readable unit string.
18
22
  * - >= 1M: one decimal, M suffix (e.g., 1200000 → 1.2M, 25000000 → 25M)
@@ -0,0 +1,16 @@
1
+ import type { AgentKind } from './types.js';
2
+ /** 一对需要同步的字段:agents.<agent> 上的配置键 ↔ agentChoices.<agent> 上的选择键。 */
3
+ export interface ChoiceField {
4
+ configKey: string;
5
+ choicesKey: string;
6
+ }
7
+ /**
8
+ * 非 claude agent 的字段映射表。
9
+ *
10
+ * claude 的配置在顶层 `config.claude`,不走 agents/agentChoices,因此不在此表中。
11
+ * syncAgentChoices(保存配置时写入 agentChoices)与 resolveAgentChoices(启动时
12
+ * 恢复)共用这张表,新增字段只需改一处。
13
+ */
14
+ export declare const AGENT_CHOICE_FIELDS: Record<Exclude<AgentKind, 'claude'>, readonly ChoiceField[]>;
15
+ /** 返回指定 agent 的字段映射;claude 或未知 agent 返回 undefined。 */
16
+ export declare function choiceFieldsFor(agent: string): readonly ChoiceField[] | undefined;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 非 claude agent 的字段映射表。
3
+ *
4
+ * claude 的配置在顶层 `config.claude`,不走 agents/agentChoices,因此不在此表中。
5
+ * syncAgentChoices(保存配置时写入 agentChoices)与 resolveAgentChoices(启动时
6
+ * 恢复)共用这张表,新增字段只需改一处。
7
+ */
8
+ export const AGENT_CHOICE_FIELDS = {
9
+ codex: [
10
+ { configKey: 'model', choicesKey: 'model' },
11
+ { configKey: 'modelProvider', choicesKey: 'modelProvider' },
12
+ ],
13
+ pi: [
14
+ { configKey: 'model', choicesKey: 'model' },
15
+ { configKey: 'provider', choicesKey: 'provider' },
16
+ { configKey: 'thinking', choicesKey: 'thinking' },
17
+ ],
18
+ opencode: [
19
+ { configKey: 'modelID', choicesKey: 'modelID' },
20
+ { configKey: 'providerID', choicesKey: 'providerID' },
21
+ ],
22
+ kimi: [
23
+ { configKey: 'model', choicesKey: 'model' },
24
+ { configKey: 'thinkingEffort', choicesKey: 'thinkingEffort' },
25
+ ],
26
+ };
27
+ /** 返回指定 agent 的字段映射;claude 或未知 agent 返回 undefined。 */
28
+ export function choiceFieldsFor(agent) {
29
+ return AGENT_CHOICE_FIELDS[agent];
30
+ }
31
+ //# sourceMappingURL=agent-choices-common.js.map
@@ -1,16 +1,21 @@
1
- import { type AgentEvent, type SpawnOptions, type AgentSessionReader, type AgentRunner as IAgentRunner, type AgentStatusInfo } from '../types.js';
2
- import { SpawningRunner } from '../common/spawning-runner.js';
1
+ import { type AgentEvent, type SpawnOptions, type AgentSessionReader, type AgentRunner, type AgentStatusInfo } from '../types.js';
3
2
  /**
4
- * Claude Agent Runner
3
+ * Claude Agent Runner(长驻交互模式)。
5
4
  *
6
- * 使用 Claude Code CLI (claude) 作为后端 AI 代理
5
+ * 权限模式(permissionMode)在 spawn 时经 --permission-mode 生效:
6
+ * - 默认 bypassPermissions:claude 内部自动放行,无 control_request → 行为与
7
+ * 旧版完全一致(无审批卡);
8
+ * - 配置为 default/acceptEdits/manual/plan 等:工具调用经 control_request
9
+ * 上抛 → bridge ApprovalCoordinator → run 卡审批区。
7
10
  */
8
- export declare class ClaudeRunner extends SpawningRunner implements IAgentRunner {
11
+ export declare class ClaudeRunner implements AgentRunner {
9
12
  readonly kind: "claude";
13
+ readonly lifetime: "workspace";
10
14
  readonly sessionReader: AgentSessionReader;
11
- private defaultModel;
12
- private defaultEffort;
13
- private defaultSettings?;
15
+ private readonly session;
16
+ private readonly defaultModel;
17
+ private readonly defaultEffort;
18
+ private readonly permissionMode;
14
19
  constructor(opts: {
15
20
  model?: string;
16
21
  effort?: string;
@@ -20,9 +25,41 @@ export declare class ClaudeRunner extends SpawningRunner implements IAgentRunner
20
25
  workspace: string;
21
26
  spawnHeartbeatMs?: number;
22
27
  sessionReader?: AgentSessionReader;
28
+ /** Claude 权限模式(官方 --permission-mode 枚举;'default'=省略参数)。 */
29
+ permissionMode?: string;
30
+ /** 会话级空闲回收 TTL(ms);0=禁用。 */
31
+ idleTtlMs?: number;
23
32
  });
33
+ get isRunning(): boolean;
24
34
  get pid(): number | undefined;
25
35
  getStatusInfo(): AgentStatusInfo;
26
- protected buildArgv(opts: SpawnOptions): string[];
27
- protected translate(rawEvent: unknown, _ctx: unknown): AgentEvent | AgentEvent[] | null;
36
+ /**
37
+ * 每 turn 入口:委托 ClaudeSession(确保进程 → 写消息 → 消费到 result)。
38
+ */
39
+ run(message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
40
+ /**
41
+ * 压缩当前会话:把 CLI 内建 `/compact` 命令作为一条 user 消息写入
42
+ * stream-json 通道,复用 run() 的进程回收/长驻逻辑消费到 turn 终态。
43
+ * bridge 通过鸭子类型(`'runCompact' in runner`)探测该能力,与 codex
44
+ * app-server / kimi ACP 的 runCompact 契约一致。
45
+ */
46
+ runCompact(_message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
47
+ /**
48
+ * 审批响应(bridge ApprovalCoordinator → runner):映射 ApprovalAction 到
49
+ * Claude 控制协议的 allow/deny(accept_all = allow + 本会话自动放行)。
50
+ */
51
+ respondApproval(requestId: number | string, response: unknown): Promise<void>;
52
+ stop(opts?: {
53
+ immediate?: boolean;
54
+ }): Promise<void>;
55
+ killOrphan(): void;
56
+ registerExitHandlers(): void;
57
+ unregisterExitHandlers(): void;
58
+ /**
59
+ * 进程级退出清理(SIGINT/SIGTERM/exit 分发器最终调 session.cleanupOnExit)。
60
+ * 暴露为委托 seam 供测试直接触发;生产路径由注册到分发器的 session 触发。
61
+ */
62
+ cleanupOnExit(): void;
63
+ /** 释放会话(bridge 淘汰槽位时调用,进程组终止)。 */
64
+ dispose(): Promise<void>;
28
65
  }
@@ -1,39 +1,64 @@
1
1
  /**
2
2
  * Claude Runner 实现
3
3
  *
4
- * 从 runner/index.ts 提取的 ClaudeRunner 类
4
+ * ClaudeRunner 从「一次一跑」(claude -p <msg> --permission-mode
5
+ * bypassPermissions)升级为长驻交互会话(--input-format stream-json 双向
6
+ * 通道),同时复用本项目已有的 ApprovalCoordinator + run 卡审批区。
7
+ *
8
+ * 架构:ClaudeRunner 是 workspace-lifetime 的薄包装,进程与协议细节在
9
+ * ClaudeSession(src/runner/claude/session.ts,extends SpawningRunner 复用
10
+ * pid 文件/killOrphan/ProcessStopper/心跳/退出分发器机制)。每次 run() 委托
11
+ * session:确保进程(可 --resume)→ 写 user 消息 → 消费事件直到本 turn result。
5
12
  */
6
13
  import { ClaudeSessionReader } from '../../session/claude/index.js';
7
- import { SpawningRunner } from '../common/spawning-runner.js';
14
+ import { ClaudeSession } from './session.js';
8
15
  import { MODEL_ID_TO_ALIAS } from '../../config/index.js';
9
16
  /**
10
- * Claude Agent Runner
17
+ * Claude 内建压缩命令。stream-json 输入模式下该文本会被 CLI 本地拦截为
18
+ * slash 命令(不会作为模型提示词转发),压缩 turn 以普通 result 收尾
19
+ * (真实 claude 2.1.233 实测:小会话返回 "Not enough messages to compact.",
20
+ * 有内容则返回摘要文本 + result success)。
21
+ */
22
+ const COMPACT_COMMAND = '/compact';
23
+ /**
24
+ * Claude Agent Runner(长驻交互模式)。
11
25
  *
12
- * 使用 Claude Code CLI (claude) 作为后端 AI 代理
26
+ * 权限模式(permissionMode)在 spawn 时经 --permission-mode 生效:
27
+ * - 默认 bypassPermissions:claude 内部自动放行,无 control_request → 行为与
28
+ * 旧版完全一致(无审批卡);
29
+ * - 配置为 default/acceptEdits/manual/plan 等:工具调用经 control_request
30
+ * 上抛 → bridge ApprovalCoordinator → run 卡审批区。
13
31
  */
14
- export class ClaudeRunner extends SpawningRunner {
32
+ export class ClaudeRunner {
15
33
  kind = 'claude';
34
+ lifetime = 'workspace';
16
35
  sessionReader;
36
+ session;
17
37
  defaultModel;
18
38
  defaultEffort;
19
- defaultSettings;
39
+ permissionMode;
20
40
  constructor(opts) {
21
- super({
41
+ this.defaultModel = opts.model ?? 'claude-opus-4-8';
42
+ this.defaultEffort = opts.effort ?? 'medium';
43
+ this.permissionMode = opts.permissionMode ?? 'bypassPermissions';
44
+ this.sessionReader = opts.sessionReader ?? new ClaudeSessionReader();
45
+ this.session = new ClaudeSession({
22
46
  pidDir: opts.pidDir,
23
47
  workspace: opts.workspace,
24
48
  stopGraceMs: opts.stopGraceMs,
25
49
  spawnHeartbeatMs: opts.spawnHeartbeatMs,
26
- pidFilePrefix: 'claude',
27
- logTag: 'claude-runner',
50
+ permissionMode: this.permissionMode,
51
+ settings: opts.settings,
52
+ model: this.defaultModel,
53
+ effort: this.defaultEffort,
54
+ idleTtlMs: opts.idleTtlMs,
28
55
  });
29
- this.binary = 'claude';
30
- this.defaultModel = opts.model ?? 'claude-opus-4-8';
31
- this.defaultEffort = opts.effort ?? 'medium';
32
- this.defaultSettings = opts.settings;
33
- this.sessionReader = opts.sessionReader ?? new ClaudeSessionReader();
56
+ }
57
+ get isRunning() {
58
+ return this.session.isRunning;
34
59
  }
35
60
  get pid() {
36
- return this.currentProcess?.pid;
61
+ return this.session.pid;
37
62
  }
38
63
  getStatusInfo() {
39
64
  const alias = MODEL_ID_TO_ALIAS[this.defaultModel] ?? this.defaultModel;
@@ -42,42 +67,88 @@ export class ClaudeRunner extends SpawningRunner {
42
67
  kind: 'claude',
43
68
  model: alias,
44
69
  reasoning: isHaiku ? 'off' : this.defaultEffort,
70
+ extras: { permissionMode: this.permissionMode },
45
71
  };
46
72
  }
47
- buildArgv(opts) {
48
- const args = [
49
- '-p',
50
- this.currentMessage,
51
- '--output-format',
52
- 'stream-json',
53
- '--verbose',
54
- // Hardcoded: Feishu chat doesn't support interactive approval.
55
- '--permission-mode',
56
- 'bypassPermissions',
57
- ];
58
- if (opts.sessionId) {
59
- args.push('--resume', opts.sessionId);
60
- }
61
- if (opts.model) {
62
- args.push('--model', opts.model);
63
- }
64
- const effort = opts.effort ?? this.defaultEffort;
65
- if (effort) {
66
- args.push('--effort', effort);
67
- }
68
- const settings = opts.settings ?? this.defaultSettings;
69
- if (settings) {
70
- args.push('--settings', settings);
73
+ /**
74
+ * 每 turn 入口:委托 ClaudeSession(确保进程 → 写消息 → 消费到 result)。
75
+ */
76
+ async *run(message, opts) {
77
+ yield* this.session.run(message, opts);
78
+ }
79
+ /**
80
+ * 压缩当前会话:把 CLI 内建 `/compact` 命令作为一条 user 消息写入
81
+ * stream-json 通道,复用 run() 的进程回收/长驻逻辑消费到 turn 终态。
82
+ * bridge 通过鸭子类型(`'runCompact' in runner`)探测该能力,与 codex
83
+ * app-server / kimi ACP 的 runCompact 契约一致。
84
+ */
85
+ async *runCompact(_message, opts) {
86
+ if (!opts.sessionId) {
87
+ throw new Error('compact requires a sessionId');
71
88
  }
72
- return args;
89
+ yield* this.session.run(COMPACT_COMMAND, opts);
73
90
  }
74
- translate(rawEvent, _ctx) {
75
- const event = rawEvent;
76
- // 统一方案:所有 runner 自己生成 timestamp
77
- if (!event.timestamp) {
78
- event.timestamp = new Date().toISOString();
91
+ /**
92
+ * 审批响应(bridge ApprovalCoordinator → runner):映射 ApprovalAction 到
93
+ * Claude 控制协议的 allow/deny(accept_all = allow + 本会话自动放行)。
94
+ */
95
+ async respondApproval(requestId, response) {
96
+ const action = response?.action ?? 'decline';
97
+ switch (action) {
98
+ case 'accept':
99
+ await this.session.respondPermission(requestId, { behavior: 'allow' });
100
+ return;
101
+ case 'accept_all':
102
+ this.session.setAutoApprove(true);
103
+ await this.session.respondPermission(requestId, { behavior: 'allow' });
104
+ return;
105
+ case 'decline':
106
+ await this.session.respondPermission(requestId, { behavior: 'deny' });
107
+ return;
108
+ case 'cancel': {
109
+ // 审批超时/取消:deny 送达后中断 turn(对齐方案验收「审批超时未响应,
110
+ // 已自动取消」终态;仅 deny 会让 claude 继续回合导致卡片 done)。
111
+ await this.session.respondPermission(requestId, {
112
+ behavior: 'deny',
113
+ message: '审批超时未响应,已自动取消。',
114
+ });
115
+ await this.session.stop({ immediate: true });
116
+ return;
117
+ }
118
+ case 'answer': {
119
+ const answers = response.answers;
120
+ if (!answers) {
121
+ throw new Error('approval answer missing answers payload');
122
+ }
123
+ await this.session.respondAskUserQuestion(requestId, answers);
124
+ return;
125
+ }
126
+ default:
127
+ await this.session.respondPermission(requestId, { behavior: 'deny' });
79
128
  }
80
- return event;
129
+ }
130
+ async stop(opts) {
131
+ await this.session.stop(opts);
132
+ }
133
+ killOrphan() {
134
+ this.session.killOrphan();
135
+ }
136
+ registerExitHandlers() {
137
+ this.session.registerExitHandlers();
138
+ }
139
+ unregisterExitHandlers() {
140
+ this.session.unregisterExitHandlers();
141
+ }
142
+ /**
143
+ * 进程级退出清理(SIGINT/SIGTERM/exit 分发器最终调 session.cleanupOnExit)。
144
+ * 暴露为委托 seam 供测试直接触发;生产路径由注册到分发器的 session 触发。
145
+ */
146
+ cleanupOnExit() {
147
+ this.session.cleanupOnExit();
148
+ }
149
+ /** 释放会话(bridge 淘汰槽位时调用,进程组终止)。 */
150
+ async dispose() {
151
+ await this.session.dispose();
81
152
  }
82
153
  }
83
154
  //# sourceMappingURL=runner.js.map
@@ -0,0 +1,166 @@
1
+ /**
2
+ * ClaudeSession: 长驻交互式 Claude Code 会话(stream-json 双向通道)。
3
+ *
4
+ * 协议(本机 claude 2.1.233 真实 spike 验证):
5
+ * - spawn 参数:
6
+ * --output-format stream-json --input-format stream-json
7
+ * --permission-prompt-tool stdio --replay-user-messages [--verbose]
8
+ * [--permission-mode <mode>] [--resume <sessionId>]
9
+ * - 用户消息(stdin):
10
+ * {"type":"user","message":{"role":"user","content":"..."}}
11
+ * - 压缩(stdin):content 为 "/compact" 时 CLI 本地拦截为 slash 命令(不会
12
+ * 作为模型提示词),压缩 turn 以普通 result 收尾(真实 claude 2.1.233 实测)。
13
+ * - 权限请求(stdout):
14
+ * {"type":"control_request","request_id":"...","request":{
15
+ * "subtype":"can_use_tool","tool_name":"Bash","input":{...}}}
16
+ * - 审批响应(stdin):
17
+ * {"type":"control_response","response":{"subtype":"success",
18
+ * "request_id":"...","response":{"behavior":"allow","updatedInput":{...}}}}
19
+ * deny 时 response={"behavior":"deny","message":"..."}
20
+ * - AskUserQuestion 走同一通道(tool_name="AskUserQuestion"),选项在
21
+ * input.questions[];答案回填 input.answers 后 allow。
22
+ * - result 事件 subtype 为 compact/compaction 是 turn 中途压缩,不是 turn 结束;
23
+ * 其余 subtype(success/error/...)才是 turn 终态。
24
+ *
25
+ * 生命周期:一个 workspace 一个长驻进程(lifetime='workspace')。每次
26
+ * run(message) = 写一条 user 消息 + 消费 stdout 事件直到本 turn 的 result;
27
+ * 进程在 turn 之间保持存活(stdin 不关闭),/stop / /new / /cd / 看门狗超时
28
+ * 时经 ProcessStopper 组杀,下条消息按 SessionStore 的 sessionId --resume。
29
+ */
30
+ import { SpawningRunner } from '../common/spawning-runner.js';
31
+ import type { AgentEvent, SpawnOptions } from '../types.js';
32
+ export interface ClaudeSessionOptions {
33
+ pidDir?: string;
34
+ workspace: string;
35
+ stopGraceMs?: number;
36
+ spawnHeartbeatMs?: number;
37
+ /** Claude 权限模式(官方 --permission-mode 枚举;'default' 省略该参数)。 */
38
+ permissionMode?: string;
39
+ settings?: string;
40
+ model?: string;
41
+ effort?: string;
42
+ /** 会话级空闲回收 TTL(ms):turn 之间无活动超过该窗口则停止进程。0=禁用。 */
43
+ idleTtlMs?: number;
44
+ }
45
+ /** 审批响应结果(behavior allow/deny)。 */
46
+ export interface PermissionResult {
47
+ behavior: 'allow' | 'deny';
48
+ message?: string;
49
+ }
50
+ /**
51
+ * ClaudeSession extends SpawningRunner 复用其 spawn/pid 文件/killOrphan/
52
+ * ProcessStopper/SpawnHeartbeat/退出分发器机制(P1-1/P1-10/P1-11 契约保持),
53
+ * 但 run() 覆盖为「长驻 + 按 turn 消费」:进程跨 turn 存活,turn 结束以
54
+ * stream-json result 事件(非 compact)为界,而不是进程退出。
55
+ */
56
+ export declare class ClaudeSession extends SpawningRunner {
57
+ private readonly permissionMode;
58
+ private readonly settings?;
59
+ private readonly defaultModel?;
60
+ private readonly defaultEffort?;
61
+ private readonly idleTtlMs;
62
+ /** 当前 turn 是否在途(防止并发 run 写乱 stdin)。 */
63
+ private turnActive;
64
+ /** 本进程是否已收到 system/init(--resume 会先重放上一轮旧 result)。 */
65
+ private sawInit;
66
+ /** stdout 流是否已结束(进程退出/崩溃)。 */
67
+ private streamEnded;
68
+ /** 最近一次 spawn 的工作目录(control_request 视图用)。 */
69
+ private cwd;
70
+ /** 最近一次 init 报告的 session_id(错误结果与视图摘要用)。 */
71
+ private sessionId;
72
+ /** 已翻译事件队列(turn 消费循环读取;turn 之间清空)。 */
73
+ private eventQueue;
74
+ private waitResolve;
75
+ /** 待审批的 control_request 原始 input(回写 updatedInput 需要)。 */
76
+ private pendingToolInputs;
77
+ /** 允许所有:后续 control_request 自动 allow(本会话进程生命周期内有效)。 */
78
+ private autoApprove;
79
+ /** 串行化 stdin 写入(用户消息与审批响应交错到达)。 */
80
+ private stdinQueue;
81
+ private startPromise;
82
+ /**
83
+ * 进程代际 token:stop()/dispose() 会先把 currentProcess 置 null,流 reader
84
+ * 的 finally 不能再用 `currentProcess === proc` 判断归属(会漏掉 streamEnded
85
+ * 唤醒,turn 循环永久挂起)。每次 spawn 递增,旧代际 reader 的残留事件与
86
+ * 收尾一律跳过。
87
+ */
88
+ private processGeneration;
89
+ private idleTimer;
90
+ /** 进程退出登记('close' 事件后 exitCode 才可靠;流结束错误路径 await 它)。 */
91
+ private processExitPromise;
92
+ constructor(opts: ClaudeSessionOptions);
93
+ get pid(): number | undefined;
94
+ /**
95
+ * 每 turn 入口:确保进程已 spawn(可带 --resume)→ 写用户消息 → 消费事件
96
+ * 直到本 turn 的 result。进程在 turn 结束后保持存活,供下一条消息复用。
97
+ */
98
+ run(message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
99
+ /**
100
+ * 写一条用户消息到 stdin(--input-format stream-json 的 user 事件)。
101
+ */
102
+ sendUserMessage(message: string): Promise<void>;
103
+ /**
104
+ * 回写审批响应(允许/拒绝)。
105
+ */
106
+ respondPermission(requestId: number | string, result: PermissionResult): Promise<void>;
107
+ /**
108
+ * 回写 AskUserQuestion 答案:updatedInput = 原 input + answers。
109
+ */
110
+ respondAskUserQuestion(requestId: number | string, answers: Record<string, string | string[]>): Promise<void>;
111
+ /** 允许所有:后续 control_request 自动 allow(本会话进程生命周期内有效)。 */
112
+ setAutoApprove(value: boolean): void;
113
+ /** 释放会话:停止进程(优雅 SIGTERM→SIGKILL)。 */
114
+ dispose(): Promise<void>;
115
+ /**
116
+ * 停止会话(覆盖基类以先清空闲回收 timer)。
117
+ */
118
+ stop(opts?: {
119
+ immediate?: boolean;
120
+ }): Promise<void>;
121
+ protected getStdio(): ('ignore' | 'pipe')[];
122
+ protected buildArgv(opts: SpawnOptions): string[];
123
+ /**
124
+ * 确保长驻进程已 spawn(首次或上次被停止后)。串行化并发调用。
125
+ * 返回 null = 成功;返回 string = spawn 失败的错误消息(调用方 yield)。
126
+ */
127
+ private startProcess;
128
+ private doStartProcess;
129
+ /**
130
+ * 后台消费 stdout JSONL → 翻译 → 入队。进程退出时清理 pid 文件并标记
131
+ * streamEnded 唤醒等待中的 turn 循环。
132
+ */
133
+ private startStreamReader;
134
+ /** 最近一次进程退出的 code/signal(流结束错误结果构造用)。 */
135
+ private exitCode;
136
+ private exitSignal;
137
+ /**
138
+ * 消费事件队列直到本 turn 的 result 事件(非 compact/compaction)。
139
+ * 消费者提前关闭生成器(bridge 循环体抛错)→ 杀进程防孤儿(P1-11 语义)。
140
+ *
141
+ * result = turn 终态(2026-08-16 真实 claude 实测:长驻模式下 result 之后
142
+ * 不再有 stdout 事件,后台任务完成是静默的;旧 -p 流程「等进程退出再收尾」
143
+ * 的 post-result drain 在长驻模式下无对应物)。
144
+ */
145
+ private consumeTurn;
146
+ /** 构造进程退出且无终态 result 时的统一错误结果(对齐 SpawningRunner 语义)。 */
147
+ private buildStreamEndedError;
148
+ private wakeWaiters;
149
+ /**
150
+ * 会话级空闲回收:turn 结束后 idleTtlMs 无新消息则停止进程(对齐 codex
151
+ * ConnectionManager 的 idle TTL;/cd 遗留的旧 cwd runner 靠它回收)。
152
+ * turn 在途时永不触发;0 表示禁用。
153
+ */
154
+ private armIdleTimer;
155
+ private clearIdleTimer;
156
+ private writeUserMessage;
157
+ private writeControlResponse;
158
+ /**
159
+ * 串行化 stdin 写入(互斥)。进程未运行/管道已毁 → 抛错(调用方按需忽略)。
160
+ */
161
+ private writeStdin;
162
+ private translateEvent;
163
+ private translateControlRequest;
164
+ /** 统一方案:所有 runner 自己生成 timestamp(缺失时补)。 */
165
+ private withTimestamp;
166
+ }