lark-remote 0.1.5 → 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 (190) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.en.md +5 -1
  3. package/README.md +10 -2
  4. package/dist/bridge/approval-coordinator.d.ts +131 -0
  5. package/dist/bridge/approval-coordinator.js +409 -0
  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 +138 -5
  9. package/dist/bridge/index.js +569 -65
  10. package/dist/bridge/queue-manager.d.ts +11 -1
  11. package/dist/bridge/queue-manager.js +20 -11
  12. package/dist/card/approval-render.d.ts +16 -0
  13. package/dist/card/approval-render.js +363 -0
  14. package/dist/card/bash-card-session.d.ts +2 -24
  15. package/dist/card/bash-card-session.js +6 -73
  16. package/dist/card/bash-renderer.js +2 -3
  17. package/dist/card/card-budget.js +29 -75
  18. package/dist/card/card-session.d.ts +48 -0
  19. package/dist/card/card-session.js +86 -0
  20. package/dist/card/card-shared.d.ts +13 -10
  21. package/dist/card/card-shared.js +27 -24
  22. package/dist/card/run-card-session.d.ts +2 -55
  23. package/dist/card/run-card-session.js +6 -104
  24. package/dist/card/run-renderer.js +140 -202
  25. package/dist/card/run-state.d.ts +38 -3
  26. package/dist/card/run-state.js +221 -11
  27. package/dist/card/text-truncate.d.ts +13 -12
  28. package/dist/card/text-truncate.js +13 -63
  29. package/dist/common/fs.d.ts +8 -0
  30. package/dist/common/fs.js +17 -0
  31. package/dist/common/guards.d.ts +0 -20
  32. package/dist/common/guards.js +0 -43
  33. package/dist/common/sleep.d.ts +2 -0
  34. package/dist/common/sleep.js +5 -0
  35. package/dist/common/truncate.d.ts +39 -0
  36. package/dist/common/truncate.js +92 -0
  37. package/dist/config/codex-config.d.ts +1 -1
  38. package/dist/config/codex-config.js +1 -1
  39. package/dist/config/dir.js +1 -1
  40. package/dist/config/index.d.ts +116 -9
  41. package/dist/config/index.js +122 -29
  42. package/dist/connector/index.d.ts +74 -2
  43. package/dist/connector/index.js +119 -1
  44. package/dist/index.js +139 -99
  45. package/dist/instance-lock.js +4 -12
  46. package/dist/order/alias-resolve.d.ts +13 -0
  47. package/dist/order/alias-resolve.js +22 -0
  48. package/dist/order/alias-store.d.ts +38 -0
  49. package/dist/order/alias-store.js +106 -0
  50. package/dist/persistence/atomic-write.d.ts +8 -2
  51. package/dist/persistence/atomic-write.js +23 -7
  52. package/dist/restart.js +2 -1
  53. package/dist/router/card-helpers.d.ts +63 -0
  54. package/dist/router/card-helpers.js +146 -0
  55. package/dist/router/config/claude.js +25 -1
  56. package/dist/router/config/codex.d.ts +1 -1
  57. package/dist/router/config/codex.js +33 -5
  58. package/dist/router/config/common/model-patch.d.ts +22 -0
  59. package/dist/router/config/common/model-patch.js +26 -0
  60. package/dist/router/config/common/render.js +9 -4
  61. package/dist/router/config/index.d.ts +12 -0
  62. package/dist/router/config/index.js +25 -1
  63. package/dist/router/config/kimi.d.ts +4 -1
  64. package/dist/router/config/kimi.js +27 -0
  65. package/dist/router/config/opencode.js +18 -5
  66. package/dist/router/config/pi.js +4 -5
  67. package/dist/router/config/types.d.ts +12 -3
  68. package/dist/router/index.d.ts +77 -17
  69. package/dist/router/index.js +455 -549
  70. package/dist/router/utils.d.ts +4 -0
  71. package/dist/router/utils.js +9 -2
  72. package/dist/runner/agent-choices-common.d.ts +16 -0
  73. package/dist/runner/agent-choices-common.js +31 -0
  74. package/dist/runner/claude/runner.d.ts +47 -10
  75. package/dist/runner/claude/runner.js +117 -46
  76. package/dist/runner/claude/session.d.ts +166 -0
  77. package/dist/runner/claude/session.js +749 -0
  78. package/dist/runner/codex/app-server/protocol-types.d.ts +519 -0
  79. package/dist/runner/codex/app-server/protocol-types.js +56 -0
  80. package/dist/runner/codex/app-server/runner.d.ts +104 -0
  81. package/dist/runner/codex/app-server/runner.js +377 -0
  82. package/dist/runner/codex/app-server/translator.d.ts +89 -0
  83. package/dist/runner/codex/app-server/translator.js +716 -0
  84. package/dist/runner/codex/index.d.ts +1 -1
  85. package/dist/runner/codex/index.js +1 -1
  86. package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
  87. package/dist/runner/common/acp/protocol-helpers.js +21 -0
  88. package/dist/runner/common/acp/protocol-types.d.ts +238 -0
  89. package/dist/runner/common/acp/protocol-types.js +75 -0
  90. package/dist/runner/common/connection-based-runner.d.ts +111 -0
  91. package/dist/runner/common/connection-based-runner.js +261 -0
  92. package/dist/runner/common/jsonrpc/client.d.ts +89 -0
  93. package/dist/runner/common/jsonrpc/client.js +243 -0
  94. package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
  95. package/dist/runner/common/jsonrpc/connection-manager.js +205 -0
  96. package/dist/runner/common/jsonrpc/transport.d.ts +50 -0
  97. package/dist/runner/common/jsonrpc/transport.js +230 -0
  98. package/dist/runner/common/jsonrpc/types.d.ts +32 -0
  99. package/dist/runner/common/jsonrpc/types.js +9 -0
  100. package/dist/runner/common/process-stopper.d.ts +2 -1
  101. package/dist/runner/common/process-stopper.js +1 -0
  102. package/dist/runner/common/runner-utils.d.ts +6 -13
  103. package/dist/runner/common/runner-utils.js +6 -47
  104. package/dist/runner/common/spawning-runner.d.ts +24 -70
  105. package/dist/runner/common/spawning-runner.js +98 -152
  106. package/dist/runner/common/usage.d.ts +2 -0
  107. package/dist/runner/common/usage.js +2 -0
  108. package/dist/runner/index.d.ts +5 -5
  109. package/dist/runner/index.js +4 -4
  110. package/dist/runner/kimi/acp/runner.d.ts +106 -0
  111. package/dist/runner/kimi/acp/runner.js +448 -0
  112. package/dist/runner/kimi/acp/translator.d.ts +124 -0
  113. package/dist/runner/kimi/acp/translator.js +336 -0
  114. package/dist/runner/kimi/index.d.ts +1 -1
  115. package/dist/runner/kimi/index.js +1 -1
  116. package/dist/runner/opencode/acp/index.d.ts +1 -0
  117. package/dist/runner/opencode/acp/index.js +2 -0
  118. package/dist/runner/opencode/acp/runner.d.ts +121 -0
  119. package/dist/runner/opencode/acp/runner.js +429 -0
  120. package/dist/runner/opencode/acp/translator.d.ts +119 -0
  121. package/dist/runner/opencode/acp/translator.js +345 -0
  122. package/dist/runner/opencode/index.d.ts +1 -1
  123. package/dist/runner/opencode/index.js +1 -1
  124. package/dist/runner/pi/index.d.ts +1 -1
  125. package/dist/runner/pi/index.js +1 -1
  126. package/dist/runner/pi/rpc/client.d.ts +52 -0
  127. package/dist/runner/pi/rpc/client.js +114 -0
  128. package/dist/runner/pi/rpc/index.d.ts +4 -0
  129. package/dist/runner/pi/rpc/index.js +4 -0
  130. package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
  131. package/dist/runner/pi/rpc/protocol-types.js +21 -0
  132. package/dist/runner/pi/rpc/runner.d.ts +80 -0
  133. package/dist/runner/pi/rpc/runner.js +199 -0
  134. package/dist/runner/pi/rpc/translator.d.ts +55 -0
  135. package/dist/runner/pi/rpc/translator.js +258 -0
  136. package/dist/runner/registry.d.ts +7 -8
  137. package/dist/runner/registry.js +3 -4
  138. package/dist/runner/resolve-agent-choices.js +18 -59
  139. package/dist/runner/sync-agent-choices.js +16 -67
  140. package/dist/runner/types.d.ts +153 -2
  141. package/dist/session/claude/session-index.js +2 -3
  142. package/dist/session/claude/session-reader.js +3 -6
  143. package/dist/session/claude/sessions.js +6 -13
  144. package/dist/session/codex/rollout-reader.d.ts +2 -2
  145. package/dist/session/codex/rollout-reader.js +57 -17
  146. package/dist/session/codex/sessions.d.ts +1 -1
  147. package/dist/session/codex/sessions.js +2 -2
  148. package/dist/session/common/content-blocks.d.ts +7 -0
  149. package/dist/session/common/content-blocks.js +13 -9
  150. package/dist/session/common/jsonl.d.ts +8 -25
  151. package/dist/session/common/jsonl.js +126 -261
  152. package/dist/session/common/pagination.d.ts +28 -0
  153. package/dist/session/common/pagination.js +33 -0
  154. package/dist/session/kimi/sessions.d.ts +34 -1
  155. package/dist/session/kimi/sessions.js +208 -98
  156. package/dist/session/opencode/sessions.d.ts +17 -3
  157. package/dist/session/opencode/sessions.js +29 -29
  158. package/dist/session/pi/sessions.d.ts +5 -1
  159. package/dist/session/pi/sessions.js +12 -16
  160. package/dist/session/session-store.d.ts +11 -1
  161. package/dist/session/session-store.js +75 -178
  162. package/dist/update/index.d.ts +2 -2
  163. package/dist/update/index.js +2 -2
  164. package/dist/update/install.d.ts +20 -2
  165. package/dist/update/install.js +51 -7
  166. package/package.json +1 -1
  167. package/dist/runner/codex/argv.d.ts +0 -33
  168. package/dist/runner/codex/argv.js +0 -40
  169. package/dist/runner/codex/jsonl.d.ts +0 -37
  170. package/dist/runner/codex/jsonl.js +0 -221
  171. package/dist/runner/codex/runner.d.ts +0 -63
  172. package/dist/runner/codex/runner.js +0 -124
  173. package/dist/runner/common/exec-translator.d.ts +0 -73
  174. package/dist/runner/common/exec-translator.js +0 -105
  175. package/dist/runner/kimi/jsonl.d.ts +0 -28
  176. package/dist/runner/kimi/jsonl.js +0 -143
  177. package/dist/runner/kimi/runner.d.ts +0 -49
  178. package/dist/runner/kimi/runner.js +0 -105
  179. package/dist/runner/opencode/argv.d.ts +0 -32
  180. package/dist/runner/opencode/argv.js +0 -35
  181. package/dist/runner/opencode/jsonl.d.ts +0 -68
  182. package/dist/runner/opencode/jsonl.js +0 -295
  183. package/dist/runner/opencode/runner.d.ts +0 -61
  184. package/dist/runner/opencode/runner.js +0 -93
  185. package/dist/runner/pi/jsonl.d.ts +0 -128
  186. package/dist/runner/pi/jsonl.js +0 -249
  187. package/dist/runner/pi/runner.d.ts +0 -36
  188. package/dist/runner/pi/runner.js +0 -76
  189. package/dist/session/common/catch-up.d.ts +0 -12
  190. package/dist/session/common/catch-up.js +0 -31
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Shared text truncation primitives.
3
+ *
4
+ * Centralizes the two truncation utilities previously split across the card
5
+ * layer (`card-shared.ts` char-based `truncate` and `card/text-truncate.ts`
6
+ * byte-safe `truncateUtf8`). Session readers import from here instead of the
7
+ * card layer (fixing the layer inversion), and the card layer re-exports for
8
+ * backward compatibility.
9
+ */
10
+ /** Default truncation suffix, shared across all consumers for UX consistency. */
11
+ export const DEFAULT_TRUNCATE_SUFFIX = '…(已截断)';
12
+ /**
13
+ * Max byte budget for a single session `tool_result` event's content when
14
+ * replaying an opencode session (L2 pre-fold). Pre-folding here bounds the
15
+ * in-memory `events[]` array and the intermediate card JSON so a single
16
+ * pathological tool_result never inflates the replay payload to megabytes.
17
+ */
18
+ export const TOOL_RESULT_MAX_BYTES = 4000;
19
+ /**
20
+ * Fit a UTF-8 string into `maxBytes` by iterating Unicode codepoints, never
21
+ * splitting a multi-byte sequence. When `fromEnd` is true, keeps the TAIL
22
+ * (useful for logs/tails); otherwise keeps the HEAD.
23
+ */
24
+ export function fitUtf8(value, maxBytes, fromEnd = false) {
25
+ if (fromEnd) {
26
+ const codepoints = Array.from(value);
27
+ let bytes = 0;
28
+ let start = codepoints.length;
29
+ for (let i = codepoints.length - 1; i >= 0; i--) {
30
+ const cb = Buffer.byteLength(codepoints[i], 'utf8');
31
+ if (bytes + cb > maxBytes)
32
+ break;
33
+ bytes += cb;
34
+ start = i;
35
+ }
36
+ return codepoints.slice(start).join('');
37
+ }
38
+ // head 分支:惰性 for-of 迭代 + 字符串索引累计(O(1) 内存)。
39
+ let bytes = 0;
40
+ let end = 0;
41
+ for (const char of value) {
42
+ const cb = Buffer.byteLength(char, 'utf8');
43
+ if (bytes + cb > maxBytes)
44
+ break;
45
+ bytes += cb;
46
+ end += char.length;
47
+ }
48
+ return value.slice(0, end);
49
+ }
50
+ /**
51
+ * Truncate a UTF-8 string to fit within `maxBytes`, appending `suffix`
52
+ * (default `…(已截断)`) when truncation occurs. When `fromEnd` is true, keeps
53
+ * the tail and prepends the suffix (useful for "show the most recent N bytes").
54
+ */
55
+ export function truncateUtf8(value, maxBytes, fromEnd = false, suffix = DEFAULT_TRUNCATE_SUFFIX) {
56
+ if (Buffer.byteLength(value, 'utf8') <= maxBytes)
57
+ return value;
58
+ const suffixBytes = Buffer.byteLength(suffix, 'utf8');
59
+ const budget = maxBytes - suffixBytes;
60
+ if (budget <= 0)
61
+ return suffix;
62
+ const truncated = fitUtf8(value, budget, fromEnd);
63
+ return fromEnd ? `${suffix}\n${truncated}` : `${truncated}${suffix}`;
64
+ }
65
+ /** Char-based truncation with optional suffix / whitespace normalization. */
66
+ export function truncate(str, max, options) {
67
+ const suffix = options?.suffix ?? '…';
68
+ const normalize = options?.normalizeWhitespace ?? false;
69
+ let s = str;
70
+ if (normalize)
71
+ s = s.replace(/\s+/g, ' ').trim();
72
+ if (s.length <= max)
73
+ return s;
74
+ const suffixLen = suffix.length;
75
+ const budget = max - suffixLen;
76
+ if (budget <= 0)
77
+ return suffix;
78
+ // Iterate by Unicode codepoint (for...of) so the cut never lands inside a
79
+ // surrogate pair (P2-31 anchor: must not emit a lone surrogate).
80
+ let end = 0;
81
+ for (const ch of s) {
82
+ if (end + ch.length > budget)
83
+ break;
84
+ end += ch.length; // 1 (BMP) or 2 (surrogate pair) — slice lands on a boundary
85
+ }
86
+ return s.slice(0, end) + suffix;
87
+ }
88
+ /** Char-level tail keep (used for store-time caps, e.g. bash output). */
89
+ export function keepTail(value, maxChars) {
90
+ return value.length <= maxChars ? value : value.slice(-maxChars);
91
+ }
92
+ //# sourceMappingURL=truncate.js.map
@@ -80,7 +80,7 @@ export declare function getCodexCatalogModels(codexHome?: string): BundledModelI
80
80
  /**
81
81
  * Test utility: clear the catalog cache. Tests that exercise the bundled
82
82
  * command path (`--bundled`) directly use `invalidateCodexBundledTestCache`
83
- * from `codex-bundled-test-helpers.ts`.
83
+ * from `tests/lib/codex-bundled-test-helpers.ts`.
84
84
  */
85
85
  export declare function invalidateCodexBundledCache(): void;
86
86
  /**
@@ -208,7 +208,7 @@ export function getCodexCatalogModels(codexHome) {
208
208
  /**
209
209
  * Test utility: clear the catalog cache. Tests that exercise the bundled
210
210
  * command path (`--bundled`) directly use `invalidateCodexBundledTestCache`
211
- * from `codex-bundled-test-helpers.ts`.
211
+ * from `tests/lib/codex-bundled-test-helpers.ts`.
212
212
  */
213
213
  export function invalidateCodexBundledCache() {
214
214
  catalogCache = null;
@@ -79,7 +79,7 @@ export function printHelp() {
79
79
  'Options:',
80
80
  ' --config-dir <path> 自定义配置目录(默认 ~/.lark-remote,可用于同机多实例)',
81
81
  ' --settings <path> 指定 Claude 配置文件路径',
82
- ' --dev 开发模式:标记从源码 bun src/index.ts 启动(空闲超时自动停止据此选择拉起方式)',
82
+ ' --dev 开发模式:标记从源码 bun src/index.ts 启动(看门狗据此选择拉起方式)',
83
83
  ' --update 升级到最新版本后退出(用于 cron/脚本自动化升级)',
84
84
  ' -h, --help 显示本帮助信息',
85
85
  ' -v, --version 显示版本号',
@@ -2,6 +2,12 @@ import { z } from 'zod';
2
2
  export { getConfigDir } from './dir.js';
3
3
  /** Valid effort values for Claude */
4
4
  export declare const CLAUDE_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max"];
5
+ /**
6
+ * Claude 官方 --permission-mode 枚举(claude --help 输出,2026-08 实测)。
7
+ * 额外支持 'default':Claude settings.json 的官方未设置值,等价于不传
8
+ * --permission-mode(交互式默认:高风险工具逐个询问)。
9
+ */
10
+ export declare const CLAUDE_PERMISSION_MODES: readonly ["default", "acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"];
5
11
  /** Valid thinking levels for Pi */
6
12
  export declare const PI_THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh"];
7
13
  /**
@@ -10,6 +16,11 @@ export declare const PI_THINKING_LEVELS: readonly ["off", "minimal", "low", "med
10
16
  * instead of re-literalizing `5000` (Clean Code P3-1, G25).
11
17
  */
12
18
  export declare const DEFAULT_STOP_GRACE_MS: 5000;
19
+ /** Codex app-server turn idle timeout default (minutes). */
20
+ export declare const DEFAULT_TURN_IDLE_TIMEOUT_MINUTES: 10;
21
+ /** 入站媒体默认配置(schema 默认与 connector 兜底共用,避免双源漂移)。 */
22
+ export declare const DEFAULT_INBOUND_MEDIA_DIR_NAME = ".lark-remote-temp";
23
+ export declare const DEFAULT_INBOUND_MEDIA_MAX_SIZE_MB = 50;
13
24
  /** 模型 ID → alias 映射。 */
14
25
  export declare const MODEL_ID_TO_ALIAS: Record<string, string>;
15
26
  /** alias → model ID 反向映射。 */
@@ -18,11 +29,22 @@ declare const ClaudeConfigSchema: z.ZodObject<{
18
29
  model: z.ZodDefault<z.ZodString>;
19
30
  effort: z.ZodDefault<z.ZodEnum<{
20
31
  medium: "medium";
21
- max: "max";
22
32
  low: "low";
23
33
  high: "high";
24
34
  xhigh: "xhigh";
35
+ max: "max";
25
36
  }>>;
37
+ permissionMode: z.ZodDefault<z.ZodEnum<{
38
+ default: "default";
39
+ plan: "plan";
40
+ bypassPermissions: "bypassPermissions";
41
+ auto: "auto";
42
+ manual: "manual";
43
+ acceptEdits: "acceptEdits";
44
+ dontAsk: "dontAsk";
45
+ }>>;
46
+ approvalTimeoutMs: z.ZodDefault<z.ZodNumber>;
47
+ idleTtlMinutes: z.ZodDefault<z.ZodNumber>;
26
48
  stopGraceMs: z.ZodDefault<z.ZodNumber>;
27
49
  }, z.core.$strip>;
28
50
  /** Codex-specific configuration. */
@@ -30,12 +52,31 @@ export declare const CodexConfigSchema: z.ZodObject<{
30
52
  model: z.ZodOptional<z.ZodString>;
31
53
  modelProvider: z.ZodOptional<z.ZodString>;
32
54
  reasoningEffort: z.ZodOptional<z.ZodString>;
33
- stopGraceMs: z.ZodDefault<z.ZodNumber>;
55
+ approvalPolicy: z.ZodDefault<z.ZodEnum<{
56
+ untrusted: "untrusted";
57
+ "on-request": "on-request";
58
+ never: "never";
59
+ }>>;
60
+ sandbox: z.ZodDefault<z.ZodEnum<{
61
+ "read-only": "read-only";
62
+ "workspace-write": "workspace-write";
63
+ "danger-full-access": "danger-full-access";
64
+ }>>;
65
+ appServer: z.ZodOptional<z.ZodObject<{
66
+ binary: z.ZodDefault<z.ZodString>;
67
+ requestTimeoutMs: z.ZodDefault<z.ZodNumber>;
68
+ idleTtlMs: z.ZodDefault<z.ZodNumber>;
69
+ turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
70
+ }, z.core.$strip>>;
34
71
  }, z.core.$strip>;
35
72
  /** OpenCode-specific configuration (run mode: opencode run --format json --auto). */
36
73
  declare const OpencodeConfigSchema: z.ZodObject<{
37
74
  providerID: z.ZodDefault<z.ZodString>;
38
75
  modelID: z.ZodDefault<z.ZodString>;
76
+ mode: z.ZodDefault<z.ZodEnum<{
77
+ plan: "plan";
78
+ build: "build";
79
+ }>>;
39
80
  }, z.core.$strip>;
40
81
  /** Pi-specific configuration. pi is a spawn-per-message CLI like Claude. */
41
82
  declare const PiConfigSchema: z.ZodObject<{
@@ -51,14 +92,25 @@ declare const PiConfigSchema: z.ZodObject<{
51
92
  }>>;
52
93
  tools: z.ZodDefault<z.ZodString>;
53
94
  }, z.core.$strip>;
54
- /** Kimi-specific configuration. kimi is a spawn-per-message CLI. */
95
+ /** Kimi-specific configuration. Pure ACP mode (persistent `kimi acp` connection). */
55
96
  declare const KimiConfigSchema: z.ZodObject<{
56
97
  model: z.ZodDefault<z.ZodString>;
57
98
  thinkingEffort: z.ZodDefault<z.ZodEnum<{
58
- max: "max";
59
99
  low: "low";
60
100
  high: "high";
101
+ max: "max";
61
102
  }>>;
103
+ permissionMode: z.ZodDefault<z.ZodEnum<{
104
+ auto: "auto";
105
+ yolo: "yolo";
106
+ manual: "manual";
107
+ }>>;
108
+ acp: z.ZodOptional<z.ZodObject<{
109
+ binary: z.ZodDefault<z.ZodString>;
110
+ requestTimeoutMs: z.ZodDefault<z.ZodNumber>;
111
+ idleTtlMs: z.ZodDefault<z.ZodNumber>;
112
+ turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
113
+ }, z.core.$strip>>;
62
114
  }, z.core.$strip>;
63
115
  export declare const AppConfigSchema: z.ZodObject<{
64
116
  feishu: z.ZodObject<{
@@ -69,23 +121,53 @@ export declare const AppConfigSchema: z.ZodObject<{
69
121
  model: z.ZodDefault<z.ZodString>;
70
122
  effort: z.ZodDefault<z.ZodEnum<{
71
123
  medium: "medium";
72
- max: "max";
73
124
  low: "low";
74
125
  high: "high";
75
126
  xhigh: "xhigh";
127
+ max: "max";
128
+ }>>;
129
+ permissionMode: z.ZodDefault<z.ZodEnum<{
130
+ default: "default";
131
+ plan: "plan";
132
+ bypassPermissions: "bypassPermissions";
133
+ auto: "auto";
134
+ manual: "manual";
135
+ acceptEdits: "acceptEdits";
136
+ dontAsk: "dontAsk";
76
137
  }>>;
138
+ approvalTimeoutMs: z.ZodDefault<z.ZodNumber>;
139
+ idleTtlMinutes: z.ZodDefault<z.ZodNumber>;
77
140
  stopGraceMs: z.ZodDefault<z.ZodNumber>;
78
141
  }, z.core.$strip>>;
79
- agents: z.ZodOptional<z.ZodObject<{
80
- codex: z.ZodOptional<z.ZodObject<{
142
+ agents: z.ZodDefault<z.ZodObject<{
143
+ codex: z.ZodDefault<z.ZodObject<{
81
144
  model: z.ZodOptional<z.ZodString>;
82
145
  modelProvider: z.ZodOptional<z.ZodString>;
83
146
  reasoningEffort: z.ZodOptional<z.ZodString>;
84
- stopGraceMs: z.ZodDefault<z.ZodNumber>;
147
+ approvalPolicy: z.ZodDefault<z.ZodEnum<{
148
+ untrusted: "untrusted";
149
+ "on-request": "on-request";
150
+ never: "never";
151
+ }>>;
152
+ sandbox: z.ZodDefault<z.ZodEnum<{
153
+ "read-only": "read-only";
154
+ "workspace-write": "workspace-write";
155
+ "danger-full-access": "danger-full-access";
156
+ }>>;
157
+ appServer: z.ZodOptional<z.ZodObject<{
158
+ binary: z.ZodDefault<z.ZodString>;
159
+ requestTimeoutMs: z.ZodDefault<z.ZodNumber>;
160
+ idleTtlMs: z.ZodDefault<z.ZodNumber>;
161
+ turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
162
+ }, z.core.$strip>>;
85
163
  }, z.core.$strip>>;
86
164
  opencode: z.ZodOptional<z.ZodObject<{
87
165
  providerID: z.ZodDefault<z.ZodString>;
88
166
  modelID: z.ZodDefault<z.ZodString>;
167
+ mode: z.ZodDefault<z.ZodEnum<{
168
+ plan: "plan";
169
+ build: "build";
170
+ }>>;
89
171
  }, z.core.$strip>>;
90
172
  pi: z.ZodOptional<z.ZodObject<{
91
173
  provider: z.ZodDefault<z.ZodString>;
@@ -103,10 +185,21 @@ export declare const AppConfigSchema: z.ZodObject<{
103
185
  kimi: z.ZodOptional<z.ZodObject<{
104
186
  model: z.ZodDefault<z.ZodString>;
105
187
  thinkingEffort: z.ZodDefault<z.ZodEnum<{
106
- max: "max";
107
188
  low: "low";
108
189
  high: "high";
190
+ max: "max";
191
+ }>>;
192
+ permissionMode: z.ZodDefault<z.ZodEnum<{
193
+ auto: "auto";
194
+ yolo: "yolo";
195
+ manual: "manual";
109
196
  }>>;
197
+ acp: z.ZodOptional<z.ZodObject<{
198
+ binary: z.ZodDefault<z.ZodString>;
199
+ requestTimeoutMs: z.ZodDefault<z.ZodNumber>;
200
+ idleTtlMs: z.ZodDefault<z.ZodNumber>;
201
+ turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
202
+ }, z.core.$strip>>;
110
203
  }, z.core.$strip>>;
111
204
  }, z.core.$strip>>;
112
205
  agentChoices: z.ZodOptional<z.ZodObject<{
@@ -144,6 +237,11 @@ export declare const AppConfigSchema: z.ZodObject<{
144
237
  error: "error";
145
238
  }>>;
146
239
  }, z.core.$strip>>;
240
+ inboundMedia: z.ZodDefault<z.ZodObject<{
241
+ enabled: z.ZodDefault<z.ZodBoolean>;
242
+ dirName: z.ZodDefault<z.ZodString>;
243
+ maxFileSizeMb: z.ZodDefault<z.ZodNumber>;
244
+ }, z.core.$strip>>;
147
245
  defaultAgent: z.ZodDefault<z.ZodEnum<{
148
246
  claude: "claude";
149
247
  codex: "codex";
@@ -175,6 +273,15 @@ export declare function mapAgentKey(key: string): string;
175
273
  * config 路径中不存在,写/删/读三条路径统一拒绝。
176
274
  */
177
275
  export declare function assertSafeKeyPart(part: string): void;
276
+ /**
277
+ * 按 dot-separated key 下钻到目标键的父容器(不含最后一段)。
278
+ *
279
+ * - create=true:中间缺失/非对象段自动补 `{}`(set 语义)
280
+ * - create=false:中间缺失/非对象段返回 undefined(get/delete 语义)
281
+ *
282
+ * 危险段(__proto__/prototype/constructor)在每段统一拒绝。
283
+ */
284
+ export declare function walkNestedContainer(target: unknown, parts: string[], create: boolean): Record<string, unknown> | undefined;
178
285
  /** Get a nested value from config by dot-separated key, e.g. "feishu.appId" */
179
286
  export declare function getConfigValue(config: AppConfig, key: string): unknown;
180
287
  /** Set a nested value in config by dot-separated key, write back to YAML file */
@@ -9,6 +9,20 @@ export { getConfigDir } from './dir.js';
9
9
  import { KIMI_THINKING_EFFORTS } from './kimi-config.js';
10
10
  /** Valid effort values for Claude */
11
11
  export const CLAUDE_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
12
+ /**
13
+ * Claude 官方 --permission-mode 枚举(claude --help 输出,2026-08 实测)。
14
+ * 额外支持 'default':Claude settings.json 的官方未设置值,等价于不传
15
+ * --permission-mode(交互式默认:高风险工具逐个询问)。
16
+ */
17
+ export const CLAUDE_PERMISSION_MODES = [
18
+ 'default',
19
+ 'acceptEdits',
20
+ 'auto',
21
+ 'bypassPermissions',
22
+ 'manual',
23
+ 'dontAsk',
24
+ 'plan',
25
+ ];
12
26
  /** Valid thinking levels for Pi */
13
27
  export const PI_THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh'];
14
28
  /** 默认值常量:schema 与 template 共享,避免双源不一致(G25 Magic Numbers)。 */
@@ -16,6 +30,11 @@ const DEFAULTS = {
16
30
  CLAUDE_MODEL: 'claude-opus-4-8',
17
31
  STOP_GRACE_MS: 5000,
18
32
  IDLE_WATCHDOG_MINUTES: 15,
33
+ TURN_IDLE_TIMEOUT_MINUTES: 10,
34
+ /** 审批超时默认 5 分钟(对齐 codex 红线:勿随意改短)。 */
35
+ APPROVAL_TIMEOUT_MS: 5 * 60 * 1000,
36
+ /** Claude 会话级空闲回收默认 30 分钟(对齐 codex appServer.idleTtlMs)。 */
37
+ CLAUDE_IDLE_TTL_MINUTES: 30,
19
38
  };
20
39
  /**
21
40
  * Default SIGTERM→SIGKILL grace period (ms). Exported so runners (SpawningRunner)
@@ -23,6 +42,11 @@ const DEFAULTS = {
23
42
  * instead of re-literalizing `5000` (Clean Code P3-1, G25).
24
43
  */
25
44
  export const DEFAULT_STOP_GRACE_MS = DEFAULTS.STOP_GRACE_MS;
45
+ /** Codex app-server turn idle timeout default (minutes). */
46
+ export const DEFAULT_TURN_IDLE_TIMEOUT_MINUTES = DEFAULTS.TURN_IDLE_TIMEOUT_MINUTES;
47
+ /** 入站媒体默认配置(schema 默认与 connector 兜底共用,避免双源漂移)。 */
48
+ export const DEFAULT_INBOUND_MEDIA_DIR_NAME = '.lark-remote-temp';
49
+ export const DEFAULT_INBOUND_MEDIA_MAX_SIZE_MB = 50;
26
50
  /** 模型 ID → alias 映射。 */
27
51
  export const MODEL_ID_TO_ALIAS = {
28
52
  'claude-opus-4-8': 'opus',
@@ -44,16 +68,37 @@ const FeishuConfigSchema = z.object({
44
68
  const ClaudeConfigSchema = z.object({
45
69
  model: z.string().default(DEFAULTS.CLAUDE_MODEL),
46
70
  effort: z.enum(CLAUDE_EFFORTS).default('medium'),
47
- // permissionMode 硬编码为 bypassPermissions(runner 内部),不通过 config 配置
71
+ /**
72
+ * Claude 权限模式(官方 --permission-mode 枚举 + 'default'=省略该参数)。
73
+ * 默认 bypassPermissions 保持现有行为(无审批卡);配置为其他值后激活
74
+ * 交互式审批(control_request → run 卡审批区)。
75
+ */
76
+ permissionMode: z.enum(CLAUDE_PERMISSION_MODES).default('bypassPermissions'),
77
+ /** 审批请求超时(ms),默认 5 分钟;超时自动发送 cancel 并中断 turn。
78
+ * 0 = 立即过期(fail-fast,避免审批永久挂起),不推荐;勿随意改短。 */
79
+ approvalTimeoutMs: z.number().int().min(0).default(DEFAULTS.APPROVAL_TIMEOUT_MS),
80
+ /** 会话级空闲回收(分钟):turn 之间无新消息超过该窗口则停止长驻进程。 */
81
+ idleTtlMinutes: z.number().int().min(0).default(DEFAULTS.CLAUDE_IDLE_TTL_MINUTES),
48
82
  stopGraceMs: z.number().int().min(0).default(DEFAULTS.STOP_GRACE_MS),
49
83
  });
84
+ /** Codex app-server connection configuration. */
85
+ const CodexAppServerConfigSchema = z.object({
86
+ /** Path to the codex binary for app-server mode. */
87
+ binary: z.string().default('codex'),
88
+ /** Request timeout in milliseconds. */
89
+ requestTimeoutMs: z.number().int().min(0).default(60000),
90
+ /** Idle TTL for connection pool in milliseconds. */
91
+ idleTtlMs: z.number().int().min(0).default(1800000),
92
+ /** Turn idle timeout in minutes: no app-server output for this long triggers
93
+ * turn/interrupt and fails the run. 0 disables the timeout. */
94
+ turnIdleTimeoutMinutes: z.number().int().min(0).default(DEFAULT_TURN_IDLE_TIMEOUT_MINUTES),
95
+ });
50
96
  /** Codex-specific configuration. */
51
97
  export const CodexConfigSchema = z.object({
52
98
  /** model to use. Undefined → codex reads from its config.toml. */
53
99
  model: z.string().optional(),
54
100
  /** model provider, e.g. 'volcengine-coding-plan'. Undefined → codex reads from its config.toml. */
55
101
  modelProvider: z.string().optional(),
56
- // approvalPolicy 和 sandboxPolicy 在 runner 内硬编码(exec 模式使用 approval_policy=never)
57
102
  /**
58
103
  * Reasoning effort level。codex 标准档位 minimal/low/medium/high/xhigh/max/ultra
59
104
  * 之外还接受 none 与模型自定义值(ReasoningEffort::Custom),目录声明什么就存什么
@@ -61,8 +106,14 @@ export const CodexConfigSchema = z.object({
61
106
  */
62
107
  // codex 拒绝空串档位(ReasoningEffort::from_str "" → Err,P3-3)
63
108
  reasoningEffort: z.string().min(1).optional(),
64
- /** Stop grace period in milliseconds. */
65
- stopGraceMs: z.number().int().min(0).default(5000),
109
+ /** Approval policy (Codex 官方 AskForApproval 标准值). 默认 on-request。 */
110
+ approvalPolicy: z.enum(['untrusted', 'on-request', 'never']).default('on-request'),
111
+ /** Sandbox mode (Codex 官方 SandboxMode 标准值). 默认 workspace-write。 */
112
+ sandbox: z
113
+ .enum(['read-only', 'workspace-write', 'danger-full-access'])
114
+ .default('workspace-write'),
115
+ /** App-server connection configuration. */
116
+ appServer: CodexAppServerConfigSchema.optional(),
66
117
  });
67
118
  /** OpenCode-specific configuration (run mode: opencode run --format json --auto). */
68
119
  const OpencodeConfigSchema = z.object({
@@ -70,6 +121,8 @@ const OpencodeConfigSchema = z.object({
70
121
  providerID: z.string().default('anthropic'),
71
122
  /** Model ID for the LLM backend (e.g. 'claude-sonnet-4-20250514'). */
72
123
  modelID: z.string().default('claude-sonnet-4-20250514'),
124
+ /** Session mode (opencode agent name): 'build' (default) or 'plan'. */
125
+ mode: z.enum(['build', 'plan']).default('build'),
73
126
  });
74
127
  /** Pi-specific configuration. pi is a spawn-per-message CLI like Claude. */
75
128
  const PiConfigSchema = z.object({
@@ -82,16 +135,35 @@ const PiConfigSchema = z.object({
82
135
  /** Comma-separated tool whitelist. */
83
136
  tools: z.string().default('read,bash,edit,write,grep,find,ls'),
84
137
  });
85
- /** Kimi-specific configuration. kimi is a spawn-per-message CLI. */
138
+ /** Kimi-specific configuration. Pure ACP mode (persistent `kimi acp` connection). */
86
139
  const KimiConfigSchema = z.object({
87
140
  /** Model ID or alias, e.g. 'kimi-code/k3'. */
88
141
  model: z.string().default('kimi-code/k3'),
89
142
  /** Thinking effort: 'on', 'max'. */
90
143
  thinkingEffort: z.enum(KIMI_THINKING_EFFORTS).default('max'),
144
+ /** Permission mode: 'manual' (approve each), 'auto' (engine decides), 'yolo' (allow all). */
145
+ permissionMode: z.enum(['manual', 'auto', 'yolo']).default('manual'),
146
+ /** ACP sub-configuration. */
147
+ acp: z
148
+ .object({
149
+ /** Path to kimi binary for ACP mode. */
150
+ binary: z.string().default('kimi'),
151
+ /** Request timeout in milliseconds. */
152
+ requestTimeoutMs: z.number().int().min(0).default(60000),
153
+ /** Idle TTL for ACP connection in milliseconds. */
154
+ idleTtlMs: z.number().int().min(0).default(1800000),
155
+ /** Turn idle timeout in minutes (0 disables). */
156
+ turnIdleTimeoutMinutes: z.number().int().min(0).default(DEFAULT_TURN_IDLE_TIMEOUT_MINUTES),
157
+ })
158
+ .optional(),
91
159
  });
160
+ /** 首次启动的默认 agents 配置:codex 默认 app-server / on-request / workspace-write。 */
161
+ const DEFAULT_AGENTS_CONFIG = {
162
+ codex: CodexConfigSchema.parse({}),
163
+ };
92
164
  /** Agents configuration: per-agent settings keyed by agent kind. */
93
165
  const AgentsConfigSchema = z.object({
94
- codex: CodexConfigSchema.optional(),
166
+ codex: CodexConfigSchema.default(DEFAULT_AGENTS_CONFIG.codex),
95
167
  opencode: OpencodeConfigSchema.optional(),
96
168
  pi: PiConfigSchema.optional(),
97
169
  kimi: KimiConfigSchema.optional(),
@@ -112,6 +184,15 @@ const OutputConfigSchema = z.object({
112
184
  const LoggingConfigSchema = z.object({
113
185
  level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
114
186
  });
187
+ /** 入站媒体落盘配置(预留可配置,默认即可用)。 */
188
+ const InboundMediaConfigSchema = z.object({
189
+ /** 是否启用图片/文件落盘。 */
190
+ enabled: z.boolean().default(true),
191
+ /** 落盘根目录名(位于当前 cwd 下)。 */
192
+ dirName: z.string().min(1).default(DEFAULT_INBOUND_MEDIA_DIR_NAME),
193
+ /** 单文件大小上限(MiB)。 */
194
+ maxFileSizeMb: z.number().int().min(1).default(DEFAULT_INBOUND_MEDIA_MAX_SIZE_MB),
195
+ });
115
196
  /** Agent-specific saved choices for quick switching. */
116
197
  const AgentChoicesSchema = z.object({
117
198
  codex: z
@@ -144,13 +225,15 @@ export const AppConfigSchema = z.object({
144
225
  feishu: FeishuConfigSchema,
145
226
  /** Claude config (top-level; other agents live under `agents`). */
146
227
  claude: ClaudeConfigSchema.default(ClaudeConfigSchema.parse({})),
147
- /** Per-agent configuration for non-claude agents. */
148
- agents: AgentsConfigSchema.optional(),
228
+ /** Per-agent configuration for non-claude agents. 首次启动默认含 codex(app-server / workspace-write / on-request)。 */
229
+ agents: AgentsConfigSchema.default(DEFAULT_AGENTS_CONFIG),
149
230
  /** Agent-specific saved choices for quick switching. */
150
231
  agentChoices: AgentChoicesSchema.optional(),
151
232
  idle: IdleConfigSchema.default(IdleConfigSchema.parse({})),
152
233
  output: OutputConfigSchema.default(OutputConfigSchema.parse({})),
153
234
  logging: LoggingConfigSchema.default(LoggingConfigSchema.parse({})),
235
+ /** 入站媒体(图片/文件)落盘配置。 */
236
+ inboundMedia: InboundMediaConfigSchema.default(InboundMediaConfigSchema.parse({})),
154
237
  /** Default agent for new runs. */
155
238
  defaultAgent: z.enum(['claude', 'codex', 'opencode', 'pi', 'kimi']).default('claude'),
156
239
  /** Check for updates on bridge startup (default: false). */
@@ -163,6 +246,9 @@ const TEMPLATE = `feishu:
163
246
  claude:
164
247
  model: ${DEFAULTS.CLAUDE_MODEL}
165
248
  effort: medium
249
+ permissionMode: bypassPermissions
250
+ approvalTimeoutMs: ${DEFAULTS.APPROVAL_TIMEOUT_MS}
251
+ idleTtlMinutes: ${DEFAULTS.CLAUDE_IDLE_TTL_MINUTES}
166
252
  stopGraceMs: ${DEFAULTS.STOP_GRACE_MS}
167
253
 
168
254
  idle:
@@ -250,33 +336,45 @@ export function assertSafeKeyPart(part) {
250
336
  throw new Error(`[lark-remote] invalid config key segment: '${part}'`);
251
337
  }
252
338
  }
253
- /** 在对象上按 dot-separated key 设置嵌套值(仅修改内存对象,不写盘)。 */
254
- function setNestedValue(target, key, value) {
255
- const parts = key.split('.');
339
+ /**
340
+ * 按 dot-separated key 下钻到目标键的父容器(不含最后一段)。
341
+ *
342
+ * - create=true:中间缺失/非对象段自动补 `{}`(set 语义)
343
+ * - create=false:中间缺失/非对象段返回 undefined(get/delete 语义)
344
+ *
345
+ * 危险段(__proto__/prototype/constructor)在每段统一拒绝。
346
+ */
347
+ export function walkNestedContainer(target, parts, create) {
256
348
  let current = target;
257
349
  for (let i = 0; i < parts.length - 1; i++) {
258
350
  assertSafeKeyPart(parts[i]);
259
351
  const next = current[parts[i]];
260
352
  if (next == null || typeof next !== 'object') {
353
+ if (!create)
354
+ return undefined;
261
355
  current[parts[i]] = {};
262
356
  }
263
357
  current = current[parts[i]];
264
358
  }
359
+ return current;
360
+ }
361
+ /** 在对象上按 dot-separated key 设置嵌套值(仅修改内存对象,不写盘)。 */
362
+ function setNestedValue(target, key, value) {
363
+ const parts = key.split('.');
364
+ const container = walkNestedContainer(target, parts, true);
265
365
  const lastPart = parts[parts.length - 1];
266
366
  assertSafeKeyPart(lastPart);
267
- current[lastPart] = value;
367
+ container[lastPart] = value;
268
368
  }
269
369
  /** Get a nested value from config by dot-separated key, e.g. "feishu.appId" */
270
370
  export function getConfigValue(config, key) {
271
371
  const parts = key.split('.');
272
- let current = config;
273
- for (const part of parts) {
274
- assertSafeKeyPart(part);
275
- if (current == null || typeof current !== 'object')
276
- return undefined;
277
- current = current[part];
278
- }
279
- return current;
372
+ const container = walkNestedContainer(config, parts, false);
373
+ if (!container)
374
+ return undefined;
375
+ const lastPart = parts[parts.length - 1];
376
+ assertSafeKeyPart(lastPart);
377
+ return container[lastPart];
280
378
  }
281
379
  /** Set a nested value in config by dot-separated key, write back to YAML file */
282
380
  export function setConfigValue(configPath, config, key, value) {
@@ -315,17 +413,12 @@ export function setConfigValues(configPath, config, updates) {
315
413
  */
316
414
  function deleteNestedValue(target, key) {
317
415
  const parts = key.split('.');
318
- let current = target;
319
- for (let i = 0; i < parts.length - 1; i++) {
320
- assertSafeKeyPart(parts[i]);
321
- const next = current[parts[i]];
322
- if (next == null || typeof next !== 'object')
323
- return;
324
- current = next;
325
- }
416
+ const container = walkNestedContainer(target, parts, false);
417
+ if (!container)
418
+ return;
326
419
  const lastPart = parts[parts.length - 1];
327
420
  assertSafeKeyPart(lastPart);
328
- delete current[lastPart];
421
+ delete container[lastPart];
329
422
  }
330
423
  export function getAgentConfig(config, kind) {
331
424
  if (kind === 'claude') {