lark-remote 0.4.0 → 0.4.1
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.
- package/CHANGELOG.md +18 -0
- package/dist/bridge/inbound-media.d.ts +13 -20
- package/dist/bridge/inbound-media.js +107 -93
- package/dist/bridge/index.d.ts +6 -6
- package/dist/bridge/index.js +6 -12
- package/dist/cli.js +5 -1
- package/dist/config/dir.d.ts +9 -0
- package/dist/config/dir.js +69 -0
- package/dist/config/index.d.ts +22 -3
- package/dist/config/index.js +25 -3
- package/dist/connector/index.d.ts +22 -1
- package/dist/connector/index.js +79 -14
- package/dist/inbound/event.d.ts +41 -0
- package/dist/inbound/event.js +11 -0
- package/dist/inbound/placeholder.d.ts +26 -0
- package/dist/inbound/placeholder.js +149 -0
- package/dist/inbound/prompt.d.ts +20 -0
- package/dist/inbound/prompt.js +47 -0
- package/dist/inbound/receipt.d.ts +28 -0
- package/dist/inbound/receipt.js +62 -0
- package/dist/inbound/turn-assembler.d.ts +63 -0
- package/dist/inbound/turn-assembler.js +233 -0
- package/dist/inbound/turn.d.ts +48 -0
- package/dist/inbound/turn.js +10 -0
- package/dist/index.js +96 -71
- package/dist/router/config/opencode.d.ts +1 -1
- package/dist/router/config/opencode.js +11 -0
- package/dist/router/index.d.ts +17 -0
- package/dist/router/index.js +37 -9
- package/dist/runner/common/connection-based-runner.js +6 -2
- package/dist/runner/kimi/acp/runner.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.js +1 -1
- package/dist/runner/opencode/acp/runner.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* OpenCode config builder — builds fields and handles field changes for OpenCode agent.
|
|
3
3
|
*/
|
|
4
4
|
import { loadOpencodeConfig } from '../../config/opencode-config.js';
|
|
5
|
+
import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
|
|
5
6
|
import { resetModelPatch } from './common/model-patch.js';
|
|
6
7
|
const MODE_OPTIONS = [
|
|
7
8
|
{ text: 'build(默认,逐项审批)', value: 'build' },
|
|
@@ -38,6 +39,16 @@ export class OpencodeConfigBuilder {
|
|
|
38
39
|
options: MODE_OPTIONS,
|
|
39
40
|
currentValue: currentMode,
|
|
40
41
|
});
|
|
42
|
+
// Turn 空闲超时(纯 ACP 对齐 kimi:卡片暴露 turnIdleTimeoutMinutes,
|
|
43
|
+
// binary/requestTimeoutMs/idleTtlMs 为 YAML-only 工程参数)
|
|
44
|
+
const currentTurnIdleTimeoutMinutes = displayConfig.agents?.opencode?.acp?.turnIdleTimeoutMinutes ??
|
|
45
|
+
DEFAULT_TURN_IDLE_TIMEOUT_MINUTES;
|
|
46
|
+
fields.push({
|
|
47
|
+
key: 'agents.opencode.acp.turnIdleTimeoutMinutes',
|
|
48
|
+
label: 'Turn 空闲超时(分钟, 0关闭)',
|
|
49
|
+
type: 'input',
|
|
50
|
+
currentValue: String(currentTurnIdleTimeoutMinutes),
|
|
51
|
+
});
|
|
41
52
|
return fields;
|
|
42
53
|
}
|
|
43
54
|
handleFieldChange(key, value, config) {
|
package/dist/router/index.d.ts
CHANGED
|
@@ -121,6 +121,11 @@ export declare class CommandRouter {
|
|
|
121
121
|
* returns its pid. Injected from index.ts; /restart is unavailable without it.
|
|
122
122
|
*/
|
|
123
123
|
private restartSpawner?;
|
|
124
|
+
/**
|
|
125
|
+
* 冲刷装配器窗口内未提交的入站 turn(/exit、/restart 干净退出前调用,
|
|
126
|
+
* 避免静默期窗口内的内容丢失)。index.ts 注入。
|
|
127
|
+
*/
|
|
128
|
+
private flushInbound?;
|
|
124
129
|
private idleTimeoutMs;
|
|
125
130
|
/** Dev mode flag: --dev means the bridge was started from source (bun src/index.ts). */
|
|
126
131
|
private devMode;
|
|
@@ -197,12 +202,24 @@ export declare class CommandRouter {
|
|
|
197
202
|
* The reader is fetched from the registry each time to reflect config changes.
|
|
198
203
|
*/
|
|
199
204
|
private get sessionReader();
|
|
205
|
+
/**
|
|
206
|
+
* 注入入站装配器的冲刷回调(/exit、/restart 干净退出前调用,
|
|
207
|
+
* 避免静默期窗口内未提交的 turn 丢失)。index.ts 在构造后接线。
|
|
208
|
+
*/
|
|
209
|
+
setInboundFlusher(flush: () => Promise<void>): void;
|
|
200
210
|
/**
|
|
201
211
|
* Route a message: if it starts with /, handle as command; if it starts with !, execute as bash; otherwise forward to Claude.
|
|
212
|
+
*
|
|
213
|
+
* 命令识别前置条件(2026-09-15 P0 修复):
|
|
214
|
+
* 裸前缀判定会被结构占位符命中 —— SDK 把富文本里的图片渲染成 ``,
|
|
215
|
+
* 首字符恰好是 `!`,整段用户消息被当 shell 命令执行(`executeBash "[image](img_v3_…"`)。
|
|
216
|
+
* 因此命令前缀只在「调用方声明纯文本 + 剥离占位符后非空且无占位符 + 首字符是 / 或 !」
|
|
217
|
+
* 三条同时成立时生效,其余一律按文本转发。
|
|
202
218
|
*/
|
|
203
219
|
handle(message: string, ctx: CommandContext, opts?: {
|
|
204
220
|
cwdOverride?: string;
|
|
205
221
|
binding?: AgentBinding;
|
|
222
|
+
allowCommandPrefix?: boolean;
|
|
206
223
|
}): Promise<CommandResult | null>;
|
|
207
224
|
/**
|
|
208
225
|
* 一次别名展开:bridge 收到用户消息后、命令分发前调用。
|
package/dist/router/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { OrderStore } from '../order/index.js';
|
|
|
9
9
|
import { resolveAlias } from '../order/alias-resolve.js';
|
|
10
10
|
import { diffConfig, setNestedValue } from './config-diff.js';
|
|
11
11
|
import { getLogger } from '../logger/index.js';
|
|
12
|
+
import { stripPlaceholders } from '../inbound/placeholder.js';
|
|
12
13
|
import { activeRunUsage, clampInt } from './utils.js';
|
|
13
14
|
import { markdownDiv, buildSessionHistoryCard, paginationBar, parseJumpOffset, PAGE_JUMP_INVALID_HINT, } from './card-helpers.js';
|
|
14
15
|
import { MAX_FILE_UPLOAD_SIZE } from '../connector/file-limits.js';
|
|
@@ -342,6 +343,11 @@ export class CommandRouter {
|
|
|
342
343
|
* returns its pid. Injected from index.ts; /restart is unavailable without it.
|
|
343
344
|
*/
|
|
344
345
|
restartSpawner;
|
|
346
|
+
/**
|
|
347
|
+
* 冲刷装配器窗口内未提交的入站 turn(/exit、/restart 干净退出前调用,
|
|
348
|
+
* 避免静默期窗口内的内容丢失)。index.ts 注入。
|
|
349
|
+
*/
|
|
350
|
+
flushInbound;
|
|
345
351
|
idleTimeoutMs;
|
|
346
352
|
/** Dev mode flag: --dev means the bridge was started from source (bun src/index.ts). */
|
|
347
353
|
devMode;
|
|
@@ -416,13 +422,33 @@ export class CommandRouter {
|
|
|
416
422
|
get sessionReader() {
|
|
417
423
|
return this.sessionReaderRegistry.get(this.config.defaultAgent);
|
|
418
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* 注入入站装配器的冲刷回调(/exit、/restart 干净退出前调用,
|
|
427
|
+
* 避免静默期窗口内未提交的 turn 丢失)。index.ts 在构造后接线。
|
|
428
|
+
*/
|
|
429
|
+
setInboundFlusher(flush) {
|
|
430
|
+
this.flushInbound = flush;
|
|
431
|
+
}
|
|
419
432
|
/**
|
|
420
433
|
* Route a message: if it starts with /, handle as command; if it starts with !, execute as bash; otherwise forward to Claude.
|
|
434
|
+
*
|
|
435
|
+
* 命令识别前置条件(2026-09-15 P0 修复):
|
|
436
|
+
* 裸前缀判定会被结构占位符命中 —— SDK 把富文本里的图片渲染成 ``,
|
|
437
|
+
* 首字符恰好是 `!`,整段用户消息被当 shell 命令执行(`executeBash "[image](img_v3_…"`)。
|
|
438
|
+
* 因此命令前缀只在「调用方声明纯文本 + 剥离占位符后非空且无占位符 + 首字符是 / 或 !」
|
|
439
|
+
* 三条同时成立时生效,其余一律按文本转发。
|
|
421
440
|
*/
|
|
422
441
|
async handle(message, ctx, opts) {
|
|
423
442
|
const trimmed = message.trim();
|
|
424
|
-
|
|
425
|
-
|
|
443
|
+
// 双保险:任何调用点都必须先剥离结构占位符,再判命令前缀
|
|
444
|
+
//(index.ts 传 allowCommandPrefix = rawContentType === 'text',但那层不是唯一入口)。
|
|
445
|
+
const guard = stripPlaceholders(trimmed);
|
|
446
|
+
// 默认 true:order.exec 等既有内部调用点传的是用户已确认的指令文本,
|
|
447
|
+
// 且剥离后无占位符 —— 默认放行不改变它们的语义(见 command-guard.test.ts)。
|
|
448
|
+
const allowCommandPrefix = opts?.allowCommandPrefix ?? true;
|
|
449
|
+
const commandEligible = allowCommandPrefix && guard.kinds.length === 0 && guard.clean !== '';
|
|
450
|
+
const startsWithBang = commandEligible && guard.clean.startsWith('!');
|
|
451
|
+
const startsWithSlash = commandEligible && guard.clean.startsWith('/');
|
|
426
452
|
getLogger().info(`[router] handle message="${message.slice(0, 50)}..." trimmed="${trimmed.slice(0, 50)}..." startsWithSlash=${startsWithSlash} startsWithBang=${startsWithBang}`);
|
|
427
453
|
// 复制分身流程活跃期:一切消息(含命令)交给 clone 状态机处理,
|
|
428
454
|
// 不转发 coding agent、不进命令分发(index.ts 已在入队前拦截,这里
|
|
@@ -432,20 +458,20 @@ export class CommandRouter {
|
|
|
432
458
|
return null;
|
|
433
459
|
}
|
|
434
460
|
if (startsWithSlash) {
|
|
435
|
-
const result = await this.executeCommand(
|
|
461
|
+
const result = await this.executeCommand(guard.clean, ctx);
|
|
436
462
|
if (result) {
|
|
437
463
|
await this.bridge.sendResult(result, ctx);
|
|
438
464
|
}
|
|
439
465
|
if (this.pendingExit) {
|
|
440
|
-
//
|
|
441
|
-
await this.
|
|
466
|
+
// 干净退出前冲刷装配器窗口内未提交的入站 turn,避免静默期窗口内的内容丢失。
|
|
467
|
+
await this.flushInbound?.();
|
|
442
468
|
this.exitHandler();
|
|
443
469
|
}
|
|
444
470
|
return result;
|
|
445
471
|
}
|
|
446
472
|
// Handle bang commands (!command)
|
|
447
|
-
if (
|
|
448
|
-
const cmd =
|
|
473
|
+
if (startsWithBang) {
|
|
474
|
+
const cmd = guard.clean.slice(1).trim();
|
|
449
475
|
if (!cmd) {
|
|
450
476
|
await this.bridge.sendResult({ text: '请输入要执行的命令,例如 !ls' }, ctx);
|
|
451
477
|
return null;
|
|
@@ -456,7 +482,9 @@ export class CommandRouter {
|
|
|
456
482
|
// Forward to Claude (P1-14: pass the enqueue-time workspace through so the
|
|
457
483
|
// run uses the same cwd as the serial queue lane, even if /cd ran while
|
|
458
484
|
// the message was queued)
|
|
459
|
-
|
|
485
|
+
// 剥离占位符后再转发:agent 只该看到用户文本(附图已转成 attachments 块,
|
|
486
|
+
// 那是装配器生成的协议块,stripPlaceholders 会原样保留)。
|
|
487
|
+
await this.bridge.forwardToClaude(guard.clean, ctx, opts);
|
|
460
488
|
return null;
|
|
461
489
|
}
|
|
462
490
|
/**
|
|
@@ -607,7 +635,7 @@ export class CommandRouter {
|
|
|
607
635
|
// 必须同样消费,否则点击 /restart 按钮 spawn 成功后旧进程不退出,
|
|
608
636
|
// 新进程撞单例锁退出 → 重启两头落空(2026-08-01 红绿 anchor 锁定)。
|
|
609
637
|
if (this.pendingExit) {
|
|
610
|
-
await this.
|
|
638
|
+
await this.flushInbound?.();
|
|
611
639
|
this.exitHandler();
|
|
612
640
|
}
|
|
613
641
|
return;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { ConnectionLostError } from './jsonrpc/client.js';
|
|
2
2
|
import { getLogger } from '../../logger/index.js';
|
|
3
3
|
import { syntheticInitEvent } from './runner-utils.js';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
|
|
5
|
+
/** How long to wait for turn output notifications before failing.
|
|
6
|
+
* 30 分钟(单源于 config 的 DEFAULT_TURN_IDLE_TIMEOUT_MINUTES):ACP/app-server
|
|
7
|
+
* 类 agent 常因长工具调用(pip/npm install 等)或等待 SubAgent 长时间静默,
|
|
8
|
+
* 10 分钟会误杀正常进行中的 run。未接配置的 runner(如 opencode)直接吃此默认。 */
|
|
9
|
+
const TURN_IDLE_TIMEOUT_MS = DEFAULT_TURN_IDLE_TIMEOUT_MINUTES * 60 * 1000;
|
|
6
10
|
export class ConnectionBasedRunner {
|
|
7
11
|
kind;
|
|
8
12
|
sessionReader;
|
|
@@ -36,7 +36,7 @@ export interface KimiAcpRunnerOptions {
|
|
|
36
36
|
requestTimeoutMs?: number;
|
|
37
37
|
/** Idle TTL for connection manager. */
|
|
38
38
|
idleTtlMs?: number;
|
|
39
|
-
/** How long to wait for turn output before failing. Defaults to
|
|
39
|
+
/** How long to wait for turn output before failing. Defaults to 30 min. */
|
|
40
40
|
turnIdleTimeoutMs?: number;
|
|
41
41
|
/** §5.2: compaction 等待的沉默超时——距上次观察到任何新 compaction 记录
|
|
42
42
|
* (begin/terminal)超过该时长仍未终态才放弃(outcome unknown,卡片 error,
|
|
@@ -27,7 +27,7 @@ import { mapAnswersByIndex } from '../../question-common.js';
|
|
|
27
27
|
import { getLogger } from '../../../logger/index.js';
|
|
28
28
|
import { BaseAcpRunner } from '../../common/acp/base-acp-runner.js';
|
|
29
29
|
import { ProcessStopper } from '../../common/process-stopper.js';
|
|
30
|
-
/** §5.2: compaction
|
|
30
|
+
/** §5.2: compaction 等待的默认沉默超时(10 分钟,独立于 turn idle 默认 30 分钟)。 */
|
|
31
31
|
const COMPACT_IDLE_TIMEOUT_MS = 10 * 60_000;
|
|
32
32
|
/** Poll interval for the wire.jsonl compaction record. */
|
|
33
33
|
const COMPACT_POLL_INTERVAL_MS = 1_000;
|
|
@@ -40,7 +40,7 @@ export interface OpencodeAcpRunnerOptions {
|
|
|
40
40
|
requestTimeoutMs?: number;
|
|
41
41
|
/** Idle TTL for connection manager. */
|
|
42
42
|
idleTtlMs?: number;
|
|
43
|
-
/** How long to wait for turn output before failing. Defaults to
|
|
43
|
+
/** How long to wait for turn output before failing. Defaults to 30 min. */
|
|
44
44
|
turnIdleTimeoutMs?: number;
|
|
45
45
|
model?: string;
|
|
46
46
|
/** Configured session mode: 'build' (default) or 'plan' (opencode agent names). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|