lark-remote 0.5.19 → 0.5.20

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 (119) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/bridge/approval-actions.d.ts +1 -1
  3. package/dist/bridge/approval-actions.js +1 -1
  4. package/dist/bridge/bash-run.d.ts +71 -0
  5. package/dist/bridge/bash-run.js +217 -0
  6. package/dist/bridge/compact.d.ts +155 -0
  7. package/dist/bridge/compact.js +450 -0
  8. package/dist/bridge/final-usage.js +4 -4
  9. package/dist/bridge/inbound-media.js +1 -1
  10. package/dist/bridge/index.d.ts +97 -153
  11. package/dist/bridge/index.js +182 -809
  12. package/dist/bridge/job-watch.d.ts +68 -0
  13. package/dist/bridge/job-watch.js +146 -0
  14. package/dist/bridge/queue-manager.d.ts +3 -3
  15. package/dist/bridge/queue-manager.js +10 -29
  16. package/dist/card/approval-render.d.ts +2 -2
  17. package/dist/card/approval-render.js +109 -97
  18. package/dist/card/card-budget.js +1 -1
  19. package/dist/card/card-helpers.d.ts +3 -3
  20. package/dist/card/card-helpers.js +6 -6
  21. package/dist/card/final-answer-file.js +1 -1
  22. package/dist/card/run-renderer.d.ts +3 -3
  23. package/dist/card/run-renderer.js +34 -93
  24. package/dist/card/run-state.d.ts +1 -1
  25. package/dist/card/run-state.js +28 -90
  26. package/dist/card/tool-render.d.ts +1 -1
  27. package/dist/card/tool-render.js +1 -1
  28. package/dist/card/usage-format.d.ts +12 -0
  29. package/dist/card/usage-format.js +8 -3
  30. package/dist/cli.js +15 -3
  31. package/dist/clone.js +2 -2
  32. package/dist/config/codex-config.js +130 -106
  33. package/dist/config/index.d.ts +16 -2
  34. package/dist/config/index.js +23 -8
  35. package/dist/connector/index.js +6 -6
  36. package/dist/error-classification.js +3 -2
  37. package/dist/inbound/placeholder.d.ts +1 -1
  38. package/dist/inbound/placeholder.js +1 -1
  39. package/dist/index.js +203 -133
  40. package/dist/logger/index.js +6 -6
  41. package/dist/message-handlers.d.ts +0 -5
  42. package/dist/message-handlers.js +1 -1
  43. package/dist/model-catalog.d.ts +2 -2
  44. package/dist/model-catalog.js +3 -8
  45. package/dist/platform/identity.js +1 -1
  46. package/dist/platform/path.d.ts +1 -1
  47. package/dist/platform/path.js +1 -1
  48. package/dist/platform/sleep-blocker.js +1 -1
  49. package/dist/platform/terminator-win32.js +2 -1
  50. package/dist/router/commands/resume.js +2 -20
  51. package/dist/router/config/dsh.d.ts +4 -8
  52. package/dist/router/config/dsh.js +3 -10
  53. package/dist/router/config/kimi.js +1 -1
  54. package/dist/router/config/types.d.ts +1 -1
  55. package/dist/router/config-diff.js +1 -1
  56. package/dist/router/index.d.ts +56 -11
  57. package/dist/router/index.js +219 -200
  58. package/dist/runner/claude/session.d.ts +1 -1
  59. package/dist/runner/claude/session.js +6 -4
  60. package/dist/runner/codex/app-server/runner.d.ts +9 -2
  61. package/dist/runner/codex/app-server/runner.js +30 -25
  62. package/dist/runner/codex/app-server/translator.d.ts +0 -5
  63. package/dist/runner/codex/app-server/translator.js +4 -17
  64. package/dist/runner/common/acp/acp-approval.d.ts +1 -1
  65. package/dist/runner/common/acp/acp-approval.js +1 -1
  66. package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
  67. package/dist/runner/common/acp/base-acp-runner.js +26 -65
  68. package/dist/runner/common/acp/base-translator.d.ts +12 -2
  69. package/dist/runner/common/acp/base-translator.js +31 -9
  70. package/dist/runner/common/acp/protocol-types.d.ts +1 -1
  71. package/dist/runner/common/acp/protocol-types.js +2 -2
  72. package/dist/runner/common/connection-based-runner.d.ts +32 -1
  73. package/dist/runner/common/connection-based-runner.js +32 -2
  74. package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
  75. package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
  76. package/dist/runner/common/spawning-runner.d.ts +1 -1
  77. package/dist/runner/common/spawning-runner.js +1 -1
  78. package/dist/runner/dsh/catalog-probe.d.ts +17 -0
  79. package/dist/runner/dsh/catalog-probe.js +10 -0
  80. package/dist/runner/dsh/client.d.ts +1 -3
  81. package/dist/runner/dsh/client.js +2 -4
  82. package/dist/runner/dsh/exit-cancel.d.ts +2 -2
  83. package/dist/runner/dsh/exit-cancel.js +2 -2
  84. package/dist/runner/dsh/inflight.d.ts +1 -1
  85. package/dist/runner/dsh/inflight.js +1 -1
  86. package/dist/runner/dsh/runner.d.ts +40 -1
  87. package/dist/runner/dsh/runner.js +272 -199
  88. package/dist/runner/dsh/translator.d.ts +1 -1
  89. package/dist/runner/dsh/translator.js +1 -1
  90. package/dist/runner/index.d.ts +1 -1
  91. package/dist/runner/kimi/acp/runner.d.ts +1 -2
  92. package/dist/runner/kimi/acp/runner.js +1 -2
  93. package/dist/runner/kimi/acp/translator.d.ts +1 -1
  94. package/dist/runner/kimi/acp/translator.js +3 -4
  95. package/dist/runner/opencode/acp/runner.d.ts +2 -2
  96. package/dist/runner/opencode/acp/runner.js +4 -4
  97. package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
  98. package/dist/runner/pi/rpc/protocol-types.js +1 -1
  99. package/dist/runner/pi/rpc/runner.d.ts +3 -0
  100. package/dist/runner/pi/rpc/runner.js +27 -42
  101. package/dist/runner/types.d.ts +2 -2
  102. package/dist/runner/types.js +1 -1
  103. package/dist/session/claude/sessions.d.ts +45 -0
  104. package/dist/session/claude/sessions.js +97 -71
  105. package/dist/session/codex/rollout-reader.js +4 -4
  106. package/dist/session/common/throw-session-list-error.d.ts +10 -0
  107. package/dist/session/common/throw-session-list-error.js +16 -0
  108. package/dist/session/dsh/sessions.js +11 -5
  109. package/dist/session/kimi/sessions.d.ts +40 -1
  110. package/dist/session/kimi/sessions.js +244 -192
  111. package/dist/session/opencode/sessions.d.ts +153 -1
  112. package/dist/session/opencode/sessions.js +354 -321
  113. package/dist/session/pi/sessions.js +4 -5
  114. package/dist/session/session-store.d.ts +1 -1
  115. package/dist/session/session-store.js +1 -1
  116. package/dist/update/version-check.js +2 -1
  117. package/dist/workspace/index.d.ts +1 -1
  118. package/dist/workspace/index.js +1 -1
  119. package/package.json +1 -1
@@ -392,6 +392,123 @@ function extractCustomProvider(providers) {
392
392
  const keys = Object.keys(providers);
393
393
  return keys.find((k) => k !== 'openai' && k !== 'anthropic');
394
394
  }
395
+ /**
396
+ * 管线第 1 段(IO):读 + 解析 config.toml,不做任何语义解释。
397
+ * - missing:文件不存在;
398
+ * - parsed:TOML 解析成功(内容后续由 normalizeProviders 解释);
399
+ * - invalid:读取/解析抛错(含 TOCTOU 窗口内的文件损坏)——错误在此分类收口,
400
+ * 消费方(loadCodexConfig)据此走「解析失败」兜底,绝不向上抛异常。
401
+ */
402
+ function readCodexToml(codexHome) {
403
+ const configFile = path.join(codexHome, 'config.toml');
404
+ if (!fs.existsSync(configFile))
405
+ return { state: 'missing' };
406
+ try {
407
+ const raw = fs.readFileSync(configFile, 'utf-8');
408
+ return { state: 'parsed', parsed: TOML.parse(raw) };
409
+ }
410
+ catch {
411
+ return { state: 'invalid' };
412
+ }
413
+ }
414
+ /**
415
+ * 管线第 2 段(纯转换):把已解析的 TOML + 目录上下文归一成 provider/model 事实。
416
+ * Model filtering strategy:
417
+ * - Custom provider: model from config.toml (single model)
418
+ * - openai: all bundled models
419
+ * - anthropic: bundled models with 'claude-*' prefix
420
+ */
421
+ function normalizeProviders(parsed, ctx) {
422
+ const { catalogMode, catalogSlugs, allModels, anthropicModels } = ctx;
423
+ const modelKeyRaw = typeof parsed.model === 'string' && parsed.model.length > 0 ? parsed.model : undefined;
424
+ const currentProvider = String(parsed.model_provider ?? FALLBACK_PROVIDERS[0]);
425
+ // Extract model_providers section
426
+ const modelProviders = (parsed.model_providers ?? {});
427
+ const rawProviderNames = Object.keys(modelProviders);
428
+ if (catalogMode) {
429
+ // catalog 模式:provider 列表 = 内置 openai + [model_providers.*](对齐 codex
430
+ // merge_configured_model_providers(built_in_model_providers, cfg.model_providers),
431
+ // core/src/config/mod.rs:3655;model_provider 未声明时默认 "openai",mod.rs:3659)。
432
+ // anthropic 不是 codex 内置 provider,未显式配置时不得出现。模型列表仍只来自
433
+ // 活动目录——openai+内置 gpt-5.x 的失效路径不因 provider 合并复活。
434
+ const providerNames = rawProviderNames.includes('openai')
435
+ ? rawProviderNames
436
+ : ['openai', ...rawProviderNames];
437
+ const providerEnvKeys = { openai: 'OPENAI_API_KEY' };
438
+ for (const [name, config] of Object.entries(modelProviders)) {
439
+ if (typeof config.env_key === 'string') {
440
+ providerEnvKeys[name] = config.env_key;
441
+ }
442
+ }
443
+ // 活动目录全局:codex 运行时 provider 与模型无绑定(StaticModelsManager),
444
+ // 每个 provider 下拉都展示目录全部 slug
445
+ // 活动目录不可用(命令失败/空)时回退 [currentModel]——无目录元数据时档位/能力
446
+ // 不可信;禁止泄漏 FALLBACK_MODELS/内置目录。model 键缺失时不得虚构 'o3'。
447
+ const catalogCurrentModel = modelKeyRaw ?? catalogSlugs[0] ?? '';
448
+ const providerModelList = catalogSlugs.length > 0 ? catalogSlugs : modelKeyRaw ? [modelKeyRaw] : [];
449
+ const providerModels = {};
450
+ for (const name of providerNames) {
451
+ providerModels[name] = providerModelList;
452
+ }
453
+ const resolvedProvider = typeof parsed.model_provider === 'string' && parsed.model_provider.length > 0
454
+ ? parsed.model_provider
455
+ : 'openai';
456
+ return {
457
+ currentModel: catalogCurrentModel,
458
+ currentProvider: resolvedProvider,
459
+ providerNames,
460
+ providerEnvKeys,
461
+ providerModels,
462
+ };
463
+ }
464
+ // fallback 模式:保持原行为(openai/anthropic 内置 + bundled 模型)
465
+ const currentModel = modelKeyRaw ?? catalogSlugs[0] ?? 'o3';
466
+ const providerNames = rawProviderNames;
467
+ if (!providerNames.includes('openai')) {
468
+ providerNames.unshift('openai');
469
+ }
470
+ const customProvider = extractCustomProvider(modelProviders);
471
+ const providerEnvKeys = {};
472
+ providerEnvKeys['openai'] = 'OPENAI_API_KEY';
473
+ for (const [name, config] of Object.entries(modelProviders)) {
474
+ if (typeof config.env_key === 'string') {
475
+ providerEnvKeys[name] = config.env_key;
476
+ }
477
+ }
478
+ // anthropic 显式配置但未写 env_key 时保留默认键(恢复旧版无条件映射的
479
+ // 配置场景行为;未配置时不再虚构)
480
+ if (rawProviderNames.includes('anthropic') &&
481
+ !modelProviders['anthropic']?.env_key &&
482
+ providerEnvKeys['anthropic'] === undefined) {
483
+ providerEnvKeys['anthropic'] = 'ANTHROPIC_API_KEY';
484
+ }
485
+ const providerModels = { openai: allModels };
486
+ // anthropic 仅在用户显式配置时才存在(非 codex 内置 provider)
487
+ if (rawProviderNames.includes('anthropic')) {
488
+ providerModels['anthropic'] = anthropicModels;
489
+ }
490
+ if (customProvider) {
491
+ // Custom provider: use the model from config.toml
492
+ providerModels[customProvider] = [currentModel];
493
+ }
494
+ return {
495
+ currentModel,
496
+ currentProvider,
497
+ providerNames,
498
+ providerEnvKeys,
499
+ providerModels,
500
+ };
501
+ }
502
+ /**
503
+ * 管线第 3 段(纯装配):给 provider 事实挂上 modelOptions 查询闭包,产出
504
+ * 对外的 CodexConfigResult。
505
+ */
506
+ function applyProfiles(profile) {
507
+ return {
508
+ ...profile,
509
+ modelOptions: (provider) => buildModelOptions(profile.currentModel, profile.currentProvider, provider, profile.providerModels),
510
+ };
511
+ }
395
512
  /**
396
513
  * Read codex config.toml, extract provider and model info
397
514
  *
@@ -401,8 +518,9 @@ function extractCustomProvider(providers) {
401
518
  * - anthropic: bundled models with 'claude-*' prefix
402
519
  */
403
520
  export function loadCodexConfig(opts = {}) {
521
+ // 红线:resolveCodexHome 环境变量优先级(codexHome 参数 > $CODEX_HOME > ~/.codex)
522
+ // 只在此解析一次,下游各段共用同一 codexHome,不得重复解析。
404
523
  const codexHome = resolveCodexHome(opts.codexHome);
405
- const configFile = path.join(codexHome, 'config.toml');
406
524
  const catalogMode = isCodexCatalogMode(codexHome);
407
525
  // 统一目录来源:catalog 模式 = 活动目录(model_catalog_json 整体替换内置目录);
408
526
  // 否则 = bundled(与原行为一致)
@@ -413,111 +531,21 @@ export function loadCodexConfig(opts = {}) {
413
531
  const anthropicModels = catalogSlugs.length > 0
414
532
  ? catalogSlugs.filter((m) => m.startsWith('claude-'))
415
533
  : ANTHROPIC_FALLBACK_MODELS;
416
- if (!fs.existsSync(configFile)) {
534
+ const toml = readCodexToml(codexHome);
535
+ if (toml.state === 'missing') {
417
536
  // 无 config.toml:默认模型取目录首个可用模型(codex default_model_from_available),
418
537
  // 目录不可用时回退 'o3'(FALLBACK_MODELS 首项,非 catalog 默认)
419
538
  const currentModel = allModels[0] ?? 'o3';
420
539
  // No config file: only openai has bundled models
421
- const providerModels = {
422
- openai: allModels,
423
- };
424
- return {
540
+ return applyProfiles({
425
541
  currentModel,
426
542
  currentProvider: FALLBACK_PROVIDERS[0],
427
543
  providerNames: FALLBACK_PROVIDERS,
428
544
  providerEnvKeys: { openai: 'OPENAI_API_KEY' },
429
- providerModels,
430
- modelOptions: (provider) => buildModelOptions(currentModel, FALLBACK_PROVIDERS[0], provider, providerModels),
431
- };
432
- }
433
- try {
434
- const raw = fs.readFileSync(configFile, 'utf-8');
435
- const parsed = TOML.parse(raw);
436
- const modelKeyRaw = typeof parsed.model === 'string' && parsed.model.length > 0 ? parsed.model : undefined;
437
- const currentProvider = String(parsed.model_provider ?? FALLBACK_PROVIDERS[0]);
438
- // Extract model_providers section
439
- const modelProviders = (parsed.model_providers ?? {});
440
- const rawProviderNames = Object.keys(modelProviders);
441
- let providerNames;
442
- let providerEnvKeys;
443
- let providerModels;
444
- if (catalogMode) {
445
- // catalog 模式:provider 列表 = 内置 openai + [model_providers.*](对齐 codex
446
- // merge_configured_model_providers(built_in_model_providers, cfg.model_providers),
447
- // core/src/config/mod.rs:3655;model_provider 未声明时默认 "openai",mod.rs:3659)。
448
- // anthropic 不是 codex 内置 provider,未显式配置时不得出现。模型列表仍只来自
449
- // 活动目录——openai+内置 gpt-5.x 的失效路径不因 provider 合并复活。
450
- providerNames = rawProviderNames.includes('openai')
451
- ? rawProviderNames
452
- : ['openai', ...rawProviderNames];
453
- providerEnvKeys = { openai: 'OPENAI_API_KEY' };
454
- for (const [name, config] of Object.entries(modelProviders)) {
455
- if (typeof config.env_key === 'string') {
456
- providerEnvKeys[name] = config.env_key;
457
- }
458
- }
459
- // 活动目录全局:codex 运行时 provider 与模型无绑定(StaticModelsManager),
460
- // 每个 provider 下拉都展示目录全部 slug
461
- // 活动目录不可用(命令失败/空)时回退 [currentModel]——无目录元数据时档位/能力
462
- // 不可信;禁止泄漏 FALLBACK_MODELS/内置目录。model 键缺失时不得虚构 'o3'。
463
- const catalogCurrentModel = modelKeyRaw ?? catalogSlugs[0] ?? '';
464
- const providerModelList = catalogSlugs.length > 0 ? catalogSlugs : modelKeyRaw ? [modelKeyRaw] : [];
465
- providerModels = {};
466
- for (const name of providerNames) {
467
- providerModels[name] = providerModelList;
468
- }
469
- const resolvedProvider = typeof parsed.model_provider === 'string' && parsed.model_provider.length > 0
470
- ? parsed.model_provider
471
- : 'openai';
472
- return {
473
- currentModel: catalogCurrentModel,
474
- currentProvider: resolvedProvider,
475
- providerNames,
476
- providerEnvKeys,
477
- providerModels,
478
- modelOptions: (provider) => buildModelOptions(catalogCurrentModel, resolvedProvider, provider, providerModels),
479
- };
480
- }
481
- // fallback 模式:保持原行为(openai/anthropic 内置 + bundled 模型)
482
- const currentModel = modelKeyRaw ?? catalogSlugs[0] ?? 'o3';
483
- providerNames = rawProviderNames;
484
- if (!providerNames.includes('openai')) {
485
- providerNames.unshift('openai');
486
- }
487
- const customProvider = extractCustomProvider(modelProviders);
488
- providerEnvKeys = {};
489
- providerEnvKeys['openai'] = 'OPENAI_API_KEY';
490
- for (const [name, config] of Object.entries(modelProviders)) {
491
- if (typeof config.env_key === 'string') {
492
- providerEnvKeys[name] = config.env_key;
493
- }
494
- }
495
- // anthropic 显式配置但未写 env_key 时保留默认键(恢复旧版无条件映射的
496
- // 配置场景行为;未配置时不再虚构)
497
- if (rawProviderNames.includes('anthropic') &&
498
- !modelProviders['anthropic']?.env_key &&
499
- providerEnvKeys['anthropic'] === undefined) {
500
- providerEnvKeys['anthropic'] = 'ANTHROPIC_API_KEY';
501
- }
502
- providerModels = { openai: allModels };
503
- // anthropic 仅在用户显式配置时才存在(非 codex 内置 provider)
504
- if (rawProviderNames.includes('anthropic')) {
505
- providerModels['anthropic'] = anthropicModels;
506
- }
507
- if (customProvider) {
508
- // Custom provider: use the model from config.toml
509
- providerModels[customProvider] = [currentModel];
510
- }
511
- return {
512
- currentModel,
513
- currentProvider,
514
- providerNames,
515
- providerEnvKeys,
516
- providerModels,
517
- modelOptions: (provider) => buildModelOptions(currentModel, currentProvider, provider, providerModels),
518
- };
545
+ providerModels: { openai: allModels },
546
+ });
519
547
  }
520
- catch {
548
+ if (toml.state === 'invalid') {
521
549
  // config.toml 读取/解析失败:readModelCatalogJsonPath 对此返回
522
550
  // TOML_PARSE_FAILED_SENTINEL(isCodexCatalogMode → false),因此正常路径下
523
551
  // catalogMode 恒为 false,下面的 `if (catalogMode)` 分支只在两次读取之间
@@ -533,19 +561,15 @@ export function loadCodexConfig(opts = {}) {
533
561
  modelOptions: () => [],
534
562
  };
535
563
  }
536
- const currentModel = catalogSlugs[0] ?? 'o3';
537
- const providerModels = {
538
- openai: allModels,
539
- };
540
- return {
541
- currentModel,
564
+ return applyProfiles({
565
+ currentModel: catalogSlugs[0] ?? 'o3',
542
566
  currentProvider: FALLBACK_PROVIDERS[0],
543
567
  providerNames: FALLBACK_PROVIDERS,
544
568
  providerEnvKeys: { openai: 'OPENAI_API_KEY' },
545
- providerModels,
546
- modelOptions: (provider) => buildModelOptions(currentModel, FALLBACK_PROVIDERS[0], provider, providerModels),
547
- };
569
+ providerModels: { openai: allModels },
570
+ });
548
571
  }
572
+ return applyProfiles(normalizeProviders(toml.parsed, { catalogMode, catalogSlugs, allModels, anthropicModels }));
549
573
  }
550
574
  /**
551
575
  * Build model options for a provider.
@@ -4,7 +4,7 @@ export { getConfigDir } from './dir.js';
4
4
  export declare const CLAUDE_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max"];
5
5
  /**
6
6
  * Claude 的权限模式取值:'default' 之外都是 CLI `--permission-mode` 的真实枚举
7
- * (2026-09-20 实测 claude --help:acceptEdits | auto | bypassPermissions | manual
7
+ * (claude --help:acceptEdits | auto | bypassPermissions | manual
8
8
  * | dontAsk | plan)。
9
9
  * 'default' 是本项目自造的值,不是 CLI 的取值 —— 它表示"不传 --permission-mode",
10
10
  * 由 CLI 自己决定默认模式,转换在 runner/claude/session.ts 的 spawn 参数处。
@@ -27,7 +27,7 @@ export declare const DEFAULT_APPROVAL_TIMEOUT_MS: number;
27
27
  /**
28
28
  * 入站媒体默认配置(schema 默认与 connector 兜底共用,避免双源漂移)。
29
29
  *
30
- * 单文件上限默认 100MB:50MB 对视频偏小(2026-09-15 事故就是 mp4 被丢弃)。
30
+ * 单文件上限默认 100MB:50MB 对视频偏小(mp4 会被丢弃)。
31
31
  * 注意飞书侧 `type=file` 超过 100MB 必须用 Range 分片下载,而 SDK 的
32
32
  * `downloadResourceToFile` 不分片(必报 234037),所以 100 是实际可用上限
33
33
  * (inbound-message-matrix.md §4 第 4 条)。
@@ -93,6 +93,16 @@ declare const OpencodeConfigSchema: z.ZodObject<{
93
93
  turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
94
94
  }, z.core.$strip>>;
95
95
  }, z.core.$strip>;
96
+ /**
97
+ * pi 默认配置:schema default、src/index.ts 注册兜底与 runner 构造器兜底
98
+ * 三处共用,避免三抄漂移。
99
+ */
100
+ export declare const PI_DEFAULTS: {
101
+ readonly provider: "Volcano";
102
+ readonly model: "glm-5.2";
103
+ readonly thinking: "medium";
104
+ readonly tools: "read,bash,edit,write,grep,find,ls";
105
+ };
96
106
  /** Pi-specific configuration. pi is a spawn-per-message CLI like Claude. */
97
107
  declare const PiConfigSchema: z.ZodObject<{
98
108
  provider: z.ZodDefault<z.ZodString>;
@@ -107,6 +117,10 @@ declare const PiConfigSchema: z.ZodObject<{
107
117
  }>>;
108
118
  tools: z.ZodDefault<z.ZodString>;
109
119
  }, z.core.$strip>;
120
+ /** kimi 默认模型:schema default 与 src/index.ts 注册兜底共用,避免双抄漂移。 */
121
+ export declare const KIMI_DEFAULTS: {
122
+ readonly model: "kimi-code/k3";
123
+ };
110
124
  /** Kimi-specific configuration. Pure ACP mode (persistent `kimi acp` connection). */
111
125
  declare const KimiConfigSchema: z.ZodObject<{
112
126
  model: z.ZodDefault<z.ZodString>;
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import YAML from 'yaml';
5
5
  import { getLogger } from '../logger/index.js';
6
6
  import { atomicWrite } from '../persistence/atomic-write.js';
7
+ import { AGENT_KINDS } from '../agent-kinds.js';
7
8
  export { getConfigDir } from './dir.js';
8
9
  // Import constants from agent config files (SSOT: Layer 1 source)
9
10
  import { KIMI_THINKING_EFFORTS } from './kimi-config.js';
@@ -11,7 +12,7 @@ import { KIMI_THINKING_EFFORTS } from './kimi-config.js';
11
12
  export const CLAUDE_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
12
13
  /**
13
14
  * Claude 的权限模式取值:'default' 之外都是 CLI `--permission-mode` 的真实枚举
14
- * (2026-09-20 实测 claude --help:acceptEdits | auto | bypassPermissions | manual
15
+ * (claude --help:acceptEdits | auto | bypassPermissions | manual
15
16
  * | dontAsk | plan)。
16
17
  * 'default' 是本项目自造的值,不是 CLI 的取值 —— 它表示"不传 --permission-mode",
17
18
  * 由 CLI 自己决定默认模式,转换在 runner/claude/session.ts 的 spawn 参数处。
@@ -55,7 +56,7 @@ export const DEFAULT_APPROVAL_TIMEOUT_MS = DEFAULTS.APPROVAL_TIMEOUT_MS;
55
56
  /**
56
57
  * 入站媒体默认配置(schema 默认与 connector 兜底共用,避免双源漂移)。
57
58
  *
58
- * 单文件上限默认 100MB:50MB 对视频偏小(2026-09-15 事故就是 mp4 被丢弃)。
59
+ * 单文件上限默认 100MB:50MB 对视频偏小(mp4 会被丢弃)。
59
60
  * 注意飞书侧 `type=file` 超过 100MB 必须用 Range 分片下载,而 SDK 的
60
61
  * `downloadResourceToFile` 不分片(必报 234037),所以 100 是实际可用上限
61
62
  * (inbound-message-matrix.md §4 第 4 条)。
@@ -141,21 +142,35 @@ const OpencodeConfigSchema = z.object({
141
142
  })
142
143
  .optional(),
143
144
  });
145
+ /**
146
+ * pi 默认配置:schema default、src/index.ts 注册兜底与 runner 构造器兜底
147
+ * 三处共用,避免三抄漂移。
148
+ */
149
+ export const PI_DEFAULTS = {
150
+ provider: 'Volcano',
151
+ model: 'glm-5.2',
152
+ thinking: 'medium',
153
+ tools: 'read,bash,edit,write,grep,find,ls',
154
+ };
144
155
  /** Pi-specific configuration. pi is a spawn-per-message CLI like Claude. */
145
156
  const PiConfigSchema = z.object({
146
157
  /** LLM provider, e.g. 'Volcano', 'anthropic', 'openai'. */
147
- provider: z.string().default('Volcano'),
158
+ provider: z.string().default(PI_DEFAULTS.provider),
148
159
  /** Model ID or alias, e.g. 'glm-5.2'. */
149
- model: z.string().default('glm-5.2'),
160
+ model: z.string().default(PI_DEFAULTS.model),
150
161
  /** Thinking level: off/minimal/low/medium/high/xhigh. */
151
- thinking: z.enum(PI_THINKING_LEVELS).default('medium'),
162
+ thinking: z.enum(PI_THINKING_LEVELS).default(PI_DEFAULTS.thinking),
152
163
  /** Comma-separated tool whitelist. */
153
- tools: z.string().default('read,bash,edit,write,grep,find,ls'),
164
+ tools: z.string().default(PI_DEFAULTS.tools),
154
165
  });
166
+ /** kimi 默认模型:schema default 与 src/index.ts 注册兜底共用,避免双抄漂移。 */
167
+ export const KIMI_DEFAULTS = {
168
+ model: 'kimi-code/k3',
169
+ };
155
170
  /** Kimi-specific configuration. Pure ACP mode (persistent `kimi acp` connection). */
156
171
  const KimiConfigSchema = z.object({
157
172
  /** Model ID or alias, e.g. 'kimi-code/k3'. */
158
- model: z.string().default('kimi-code/k3'),
173
+ model: z.string().default(KIMI_DEFAULTS.model),
159
174
  /** Thinking effort,取值见 KIMI_THINKING_EFFORTS('low' | 'high' | 'max')。 */
160
175
  thinkingEffort: z.enum(KIMI_THINKING_EFFORTS).default('max'),
161
176
  /** Permission mode: 'manual' (approve each), 'auto' (engine decides), 'yolo' (allow all). */
@@ -307,7 +322,7 @@ export const AppConfigSchema = z.object({
307
322
  /** 入站媒体(图片/文件)落盘配置。 */
308
323
  inboundMedia: InboundMediaConfigSchema.default(InboundMediaConfigSchema.parse({})),
309
324
  /** Default agent for new runs. */
310
- defaultAgent: z.enum(['claude', 'codex', 'opencode', 'pi', 'kimi', 'dsh']).default('claude'),
325
+ defaultAgent: z.enum(AGENT_KINDS).default('claude'),
311
326
  /** Check for updates on lark-remote startup (default: false). */
312
327
  checkUpdateOnStartup: z.boolean().default(false),
313
328
  });
@@ -204,8 +204,8 @@ function isAccessTokenRejected(err) {
204
204
  * CardStreamController 经 throttle 延迟触发 —— `controller.update()` 调完
205
205
  * `throttle.note()` 就 resolve,不等真正的 patch。失败时是一条脱离 await 链的
206
206
  * detached rejection,只能冒泡到 `process.unhandledRejection`,调用方的
207
- * try/catch 根本挡不住。2026-09-15 00:09 的事故正是如此:卡片 patch 时
208
- * socket 被关(AxiosError `ERR_SOCKET_CLOSED`),整进程退出,正在跑的 run 陪葬。
207
+ * try/catch 根本挡不住:卡片 patch 时 socket 被关(AxiosError `ERR_SOCKET_CLOSED`),
208
+ * 整进程退出,正在跑的 run 陪葬。
209
209
  *
210
210
  * 这里在唯一的底层 patch 出口上兜住:瞬态错误重试最多 PATCH_MAX_RETRIES 次,
211
211
  * 全部失败才把最后一个错误抛出去(此时上层 classifyRejection 也会判为
@@ -287,13 +287,13 @@ export class FeishuConnector {
287
287
  },
288
288
  });
289
289
  // 卡片 patch 的统一出口(观测 + 重试):
290
- // 1) 观测(2026-08-11 run 卡定格事故):飞书业务码错误以 HTTP 200 + {code!=0}
290
+ // 1) 观测:飞书业务码错误以 HTTP 200 + {code!=0}
291
291
  // 返回时,lark SDK 正常 resolve、@larksuite/channel 的 patchCard 丢弃返回值,
292
292
  // 导致终态卡 patch 被业务层拒绝时全链路无日志无兜底。这里把业务码记 warn。
293
293
  // **不在这里抛**:流式卡片的 patch 由 throttle 触发、脱离 await 链
294
294
  // (`Throttle.fireSoon` 丢掉 doFire 的 promise),抛出即成 detached rejection。
295
295
  // 恢复链路只覆盖有 await 的调用方:见 `updateCard()` 读业务码后抛。
296
- // 2) 重试(2026-09-15 socket-close 事故):瞬态传输失败就地重试,最多
296
+ // 2) 重试:瞬态传输失败就地重试,最多
297
297
  // PATCH_MAX_RETRIES 次,不把失败直接落到 unhandledRejection 打死进程。
298
298
  // 业务码是确定性失败,不进重试循环。
299
299
  // Guard: unit-test mocks may omit rawClient; skip probe installation in that case.
@@ -316,7 +316,7 @@ export class FeishuConnector {
316
316
  return;
317
317
  // 判据是「是否携带可下载资源」(`msg.resources`),不是 `msg_type` 白名单:
318
318
  // SDK 只为 image/file/audio/video/media/sticker 五类产出非空 resources,
319
- // 枚举 msg_type 必然漏(2026-09-15 事故:mp4 被当文本转发给 agent)。
319
+ // 枚举 msg_type 必然漏(mp4 被当文本转发给 agent)。
320
320
  // 一条消息可以**同时**产出资源事件与文本事件(post 既有文字又有图),
321
321
  // 由的装配器合并成同一个 turn。
322
322
  const hasResources = msg.resources.length > 0;
@@ -398,7 +398,7 @@ export class FeishuConnector {
398
398
  });
399
399
  }
400
400
  catch (err) {
401
- getLogger().error('[feishu] inbound media detected handler failed:', err.message);
401
+ getLogger().error('[feishu] inbound media detected handler failed:', err);
402
402
  }
403
403
  }
404
404
  /**
@@ -44,8 +44,9 @@ export function classifyRejection(reason) {
44
44
  if (status !== undefined && status >= 500 && status < 600)
45
45
  return 'recoverable';
46
46
  // 传输层瞬态失败:连接/socket 级别,与进程状态无关。
47
- // 2026-09-15 00:09 的崩溃就是这条漏了 —— AxiosError `ERR_SOCKET_CLOSED`
48
- // (卡片 patch 时 socket 被关)落到 fatal → process.exit(1),正在跑的 agent run 陪葬。
47
+ // 这条必须先于业务码判定:AxiosError `ERR_SOCKET_CLOSED`(卡片 patch 时
48
+ // socket 被关)没有 HTTP status,漏判会落到 fatal → process.exit(1),
49
+ // 正在跑的 agent run 陪葬。
49
50
  if (isTransientTransportError(reason))
50
51
  return 'recoverable';
51
52
  // 飞书 API 业务错误(如 230027 外部会话无权限)—— 经 SDK throttle detach 路径逃逸的
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 入站消息结构占位符识别与剥离(纯函数,无 IO,便于表驱动测试)。
3
3
  *
4
- * 背景(事故 2026-09-15):`@larksuite/channel` 把非文本消息渲染成结构占位符,
4
+ * 背景:`@larksuite/channel` 把非文本消息渲染成结构占位符,
5
5
  * 富文本(`post`)里的图片是 `![image](img_v3_…)` —— 首字符恰好是 `!`,于是被
6
6
  * `src/router/index.ts` 的裸前缀判定当成 bash 命令执行了整段消息。
7
7
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 入站消息结构占位符识别与剥离(纯函数,无 IO,便于表驱动测试)。
3
3
  *
4
- * 背景(事故 2026-09-15):`@larksuite/channel` 把非文本消息渲染成结构占位符,
4
+ * 背景:`@larksuite/channel` 把非文本消息渲染成结构占位符,
5
5
  * 富文本(`post`)里的图片是 `![image](img_v3_…)` —— 首字符恰好是 `!`,于是被
6
6
  * `src/router/index.ts` 的裸前缀判定当成 bash 命令执行了整段消息。
7
7
  *