lark-remote 0.4.4 → 0.5.0
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 +28 -0
- package/dist/binder.d.ts +11 -10
- package/dist/binder.js +17 -16
- package/dist/bridge/approval-coordinator.d.ts +1 -1
- package/dist/bridge/approval-coordinator.js +3 -3
- package/dist/bridge/inbound-media.d.ts +1 -27
- package/dist/bridge/inbound-media.js +23 -21
- package/dist/bridge/index.d.ts +57 -57
- package/dist/bridge/index.js +136 -129
- package/dist/bridge/queue-manager.d.ts +45 -38
- package/dist/bridge/queue-manager.js +136 -136
- package/dist/card/approval-render.d.ts +1 -1
- package/dist/card/approval-render.js +6 -6
- package/dist/card/bash-card-session.d.ts +3 -3
- package/dist/card/bash-card-session.js +3 -3
- package/dist/card/bash-renderer.js +5 -5
- package/dist/card/card-session.d.ts +4 -4
- package/dist/card/card-session.js +6 -6
- package/dist/card/card-shared.d.ts +1 -1
- package/dist/card/card-shared.js +2 -2
- package/dist/card/run-card-session.js +2 -2
- package/dist/card/run-renderer.d.ts +1 -1
- package/dist/card/run-renderer.js +22 -22
- package/dist/card/run-state.d.ts +2 -2
- package/dist/card/run-state.js +3 -3
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +6 -6
- package/dist/cli.js +5 -6
- package/dist/clone.d.ts +6 -9
- package/dist/clone.js +12 -8
- package/dist/common/clamp.d.ts +2 -0
- package/dist/common/clamp.js +5 -0
- package/dist/common/fs.d.ts +4 -3
- package/dist/common/fs.js +5 -4
- package/dist/common/truncate.d.ts +1 -1
- package/dist/common/truncate.js +2 -2
- package/dist/common/ttl-cache.d.ts +2 -2
- package/dist/common/ttl-cache.js +2 -2
- package/dist/config/codex-config.d.ts +9 -9
- package/dist/config/codex-config.js +18 -18
- package/dist/config/dir.d.ts +4 -5
- package/dist/config/dir.js +12 -10
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +6 -6
- package/dist/config/opencode-config.js +3 -3
- package/dist/config/wizard.js +2 -2
- package/dist/connector/index.d.ts +5 -7
- package/dist/connector/index.js +29 -16
- package/dist/inbound/event.d.ts +1 -1
- package/dist/inbound/event.js +1 -1
- package/dist/inbound/placeholder.d.ts +2 -2
- package/dist/inbound/placeholder.js +2 -2
- package/dist/inbound/receipt.d.ts +1 -3
- package/dist/inbound/receipt.js +2 -2
- package/dist/inbound/turn-assembler.d.ts +1 -3
- package/dist/inbound/turn-assembler.js +2 -2
- package/dist/index.js +38 -30
- package/dist/instance-lock.js +8 -7
- package/dist/logger/index.d.ts +1 -1
- package/dist/logger/index.js +4 -4
- package/dist/persistence/atomic-write.d.ts +1 -1
- package/dist/persistence/atomic-write.js +9 -5
- package/dist/platform/agent-stopper.d.ts +2 -2
- package/dist/platform/identity.d.ts +7 -1
- package/dist/platform/identity.js +2 -2
- package/dist/platform/path.d.ts +6 -0
- package/dist/platform/path.js +14 -8
- package/dist/platform/select.d.ts +1 -1
- package/dist/platform/select.js +1 -1
- package/dist/platform/shell.d.ts +2 -3
- package/dist/platform/spawn.d.ts +2 -2
- package/dist/platform/spawn.js +2 -2
- package/dist/platform/terminator-posix.js +6 -6
- package/dist/platform/terminator-win32.js +3 -3
- package/dist/platform/terminator.d.ts +3 -3
- package/dist/platform/terminator.js +2 -2
- package/dist/platform/types.d.ts +1 -1
- package/dist/restart.d.ts +1 -1
- package/dist/restart.js +5 -3
- package/dist/router/card-helpers.d.ts +1 -3
- package/dist/router/card-helpers.js +2 -2
- package/dist/router/config/codex.d.ts +1 -1
- package/dist/router/config/codex.js +3 -3
- package/dist/router/config/common/model-patch.d.ts +1 -2
- package/dist/router/config/common/model-patch.js +1 -2
- package/dist/router/config/opencode.js +2 -2
- package/dist/router/config/pi.js +1 -1
- package/dist/router/index.d.ts +30 -30
- package/dist/router/index.js +103 -84
- package/dist/router/order-exec-dispatch.d.ts +3 -10
- package/dist/router/order-exec-dispatch.js +5 -5
- package/dist/router/{utils.d.ts → usage-format.d.ts} +2 -4
- package/dist/router/{utils.js → usage-format.js} +3 -8
- package/dist/runner/bash/runner.d.ts +2 -2
- package/dist/runner/bash/runner.js +6 -6
- package/dist/runner/claude/runner.d.ts +1 -1
- package/dist/runner/claude/runner.js +2 -3
- package/dist/runner/claude/session.d.ts +4 -6
- package/dist/runner/claude/session.js +21 -23
- package/dist/runner/codex/app-server/runner.js +3 -3
- package/dist/runner/codex/app-server/translator.d.ts +2 -2
- package/dist/runner/codex/app-server/translator.js +5 -5
- package/dist/runner/common/acp/{protocol-helpers.d.ts → acp-approval.d.ts} +4 -10
- package/dist/runner/common/acp/{protocol-helpers.js → acp-approval.js} +7 -6
- package/dist/runner/common/acp/base-acp-runner.d.ts +1 -1
- package/dist/runner/common/acp/base-acp-runner.js +2 -2
- package/dist/runner/common/acp/base-translator.d.ts +3 -3
- package/dist/runner/common/acp/base-translator.js +4 -4
- package/dist/runner/common/acp/protocol-types.js +1 -1
- package/dist/runner/common/connection-based-runner.d.ts +5 -5
- package/dist/runner/common/connection-based-runner.js +5 -5
- package/dist/runner/common/jsonl-stream.d.ts +2 -2
- package/dist/runner/common/jsonl-stream.js +12 -12
- package/dist/runner/common/jsonrpc/client.d.ts +1 -1
- package/dist/runner/common/jsonrpc/client.js +1 -1
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +13 -13
- package/dist/runner/common/jsonrpc/connection-manager.js +37 -37
- package/dist/runner/common/jsonrpc/transport.d.ts +1 -1
- package/dist/runner/common/jsonrpc/transport.js +2 -2
- package/dist/runner/common/{usage.d.ts → result-usage.d.ts} +1 -1
- package/dist/runner/common/{usage.js → result-usage.js} +2 -2
- package/dist/runner/common/spawning-runner.d.ts +23 -10
- package/dist/runner/common/spawning-runner.js +48 -22
- package/dist/runner/common/{runner-utils.d.ts → synthetic-events.d.ts} +4 -4
- package/dist/runner/common/{runner-utils.js → synthetic-events.js} +5 -5
- package/dist/runner/dsh/client.d.ts +1 -1
- package/dist/runner/dsh/client.js +1 -1
- package/dist/runner/dsh/runner.js +3 -3
- package/dist/runner/kimi/acp/runner.d.ts +4 -4
- package/dist/runner/kimi/acp/runner.js +10 -10
- package/dist/runner/kimi/acp/translator.d.ts +2 -2
- package/dist/runner/kimi/acp/translator.js +5 -5
- package/dist/runner/opencode/acp/runner.d.ts +4 -4
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/opencode/acp/translator.js +2 -2
- package/dist/runner/pi/rpc/runner.d.ts +1 -1
- package/dist/runner/pi/rpc/runner.js +2 -2
- package/dist/runner/pi/rpc/translator.d.ts +1 -2
- package/dist/runner/pi/rpc/translator.js +4 -6
- package/dist/runner/registry.d.ts +3 -3
- package/dist/runner/registry.js +3 -3
- package/dist/runner/types.d.ts +1 -1
- package/dist/session/claude/session-index.d.ts +13 -1
- package/dist/session/claude/session-index.js +21 -6
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/session-reader.js +1 -1
- package/dist/session/claude/sessions.js +10 -10
- package/dist/session/codex/rollout-reader.d.ts +4 -4
- package/dist/session/codex/rollout-reader.js +14 -14
- package/dist/session/common/two-pass.js +2 -2
- package/dist/session/common/usage-accumulator.d.ts +2 -2
- package/dist/session/common/usage-accumulator.js +2 -2
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +3 -3
- package/dist/session/kimi/sessions.d.ts +2 -6
- package/dist/session/kimi/sessions.js +13 -19
- package/dist/session/opencode/sessions.d.ts +3 -3
- package/dist/session/opencode/sessions.js +15 -13
- package/dist/session/pi/sessions.js +13 -12
- package/dist/session/registry.d.ts +1 -1
- package/dist/session/registry.js +1 -1
- package/dist/session/session-store.d.ts +3 -4
- package/dist/session/session-store.js +3 -5
- package/dist/update/index.d.ts +1 -1
- package/dist/update/index.js +1 -1
- package/dist/update/version-check.d.ts +5 -3
- package/dist/update/version-check.js +10 -8
- package/dist/workspace/index.d.ts +4 -4
- package/dist/workspace/index.js +5 -5
- package/package.json +1 -1
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { CommandRouter } from './index.js';
|
|
2
|
-
import type { Bridge } from '../bridge/index.js';
|
|
3
|
-
/** Context shared by all card-action dispatch paths (file-local; callers pass it structurally). */
|
|
4
|
-
interface DispatchContext {
|
|
5
|
-
userId: string;
|
|
6
|
-
chatId: string;
|
|
7
|
-
messageId: string;
|
|
8
|
-
}
|
|
2
|
+
import type { Bridge, CommandContext } from '../bridge/index.js';
|
|
9
3
|
/**
|
|
10
4
|
* Dispatch an `order.exec` card action as an equivalent queued user message
|
|
11
5
|
* (Plan A): resolve the order text at the enqueue boundary, then enqueue it
|
|
@@ -23,8 +17,7 @@ interface DispatchContext {
|
|
|
23
17
|
export declare function dispatchOrderExecForQueue(args: {
|
|
24
18
|
router: CommandRouter;
|
|
25
19
|
bridge: Bridge;
|
|
26
|
-
|
|
20
|
+
cwd: string;
|
|
27
21
|
orderId: string | undefined;
|
|
28
|
-
ctx:
|
|
22
|
+
ctx: CommandContext;
|
|
29
23
|
}): Promise<'enqueued' | 'missing-id' | 'not-found'>;
|
|
30
|
-
export {};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* for the caller to log; performs its own error feedback via `bridge.sendResult`.
|
|
14
14
|
*/
|
|
15
15
|
export async function dispatchOrderExecForQueue(args) {
|
|
16
|
-
const { router, bridge,
|
|
16
|
+
const { router, bridge, cwd, orderId, ctx } = args;
|
|
17
17
|
if (!orderId) {
|
|
18
18
|
await bridge.sendResult({ text: '卡片 payload 缺少必要信息' }, ctx);
|
|
19
19
|
return 'missing-id';
|
|
@@ -23,12 +23,12 @@ export async function dispatchOrderExecForQueue(args) {
|
|
|
23
23
|
await bridge.sendResult({ text: '指令不存在或已被删除' }, ctx);
|
|
24
24
|
return 'not-found';
|
|
25
25
|
}
|
|
26
|
-
// Step4
|
|
26
|
+
// Step4/入队时刻快照 agent+session,随任务闭包带到执行时刻,
|
|
27
27
|
// 避免排队期间 /new、/config 改写 live 状态导致语义漂移。唯一捕获点
|
|
28
28
|
// Bridge.currentBinding。
|
|
29
29
|
const binding = bridge.currentBinding(ctx.userId);
|
|
30
|
-
bridge.enqueue(
|
|
31
|
-
await router.handle(resolved.orderText, ctx, { cwdOverride:
|
|
30
|
+
bridge.enqueue(cwd, async () => {
|
|
31
|
+
await router.handle(resolved.orderText, ctx, { cwdOverride: cwd, binding });
|
|
32
32
|
}, {
|
|
33
33
|
taskMeta: {
|
|
34
34
|
userId: ctx.userId,
|
|
@@ -38,7 +38,7 @@ export async function dispatchOrderExecForQueue(args) {
|
|
|
38
38
|
// The internal key is NOT a valid Feishu message id, so the queue
|
|
39
39
|
// status card must reply to the real Feishu card message id instead.
|
|
40
40
|
feishuReplyTo: ctx.messageId,
|
|
41
|
-
|
|
41
|
+
///Step4:binding 随 taskMeta 存进 QueuedTask,供替换闭包
|
|
42
42
|
// (queue.edit/queue.immediate)复用,不重新快照。
|
|
43
43
|
binding,
|
|
44
44
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Format session/run usage stats (context, compact count, tokens, cost) into
|
|
3
|
+
* the display strings shown on cards. Pure — no Router state.
|
|
4
4
|
*/
|
|
5
5
|
import type { ActiveRunSnapshot } from '../bridge/index.js';
|
|
6
6
|
export interface SessionDisplayUsage {
|
|
@@ -12,8 +12,6 @@ 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;
|
|
17
15
|
/**
|
|
18
16
|
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
19
17
|
* Each stat on its own line with blank lines between sections.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Format session/run usage stats (context, compact count, tokens, cost) into
|
|
3
|
+
* the display strings shown on cards. Pure — no Router state.
|
|
4
4
|
*/
|
|
5
5
|
export function activeRunUsage(activeRun) {
|
|
6
6
|
if (!activeRun || activeRun.contextLength === undefined)
|
|
@@ -12,11 +12,6 @@ export function activeRunUsage(activeRun) {
|
|
|
12
12
|
cacheCreationTokens: activeRun.cacheCreationTokens,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
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
|
-
}
|
|
20
15
|
/**
|
|
21
16
|
* Format number to human-readable unit string.
|
|
22
17
|
* - >= 1M: one decimal, M suffix (e.g., 1200000 → 1.2M, 25000000 → 25M)
|
|
@@ -154,4 +149,4 @@ export function formatUsageStats(usage, options) {
|
|
|
154
149
|
}
|
|
155
150
|
return lines.join('\n');
|
|
156
151
|
}
|
|
157
|
-
//# sourceMappingURL=
|
|
152
|
+
//# sourceMappingURL=usage-format.js.map
|
|
@@ -28,7 +28,7 @@ export interface BashRunner {
|
|
|
28
28
|
export declare class BashProcessRunner implements BashRunner {
|
|
29
29
|
private currentProcess;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* 背压高水位/低水位(层④,参照 jsonl-stream.ts 的 pauseThreshold/
|
|
32
32
|
* resumeThreshold)。stdout/stderr 队列超过高水位时 pause 流(内存有界),
|
|
33
33
|
* 消费者 drain 到低水位后 resume。构造可注入以便测试用小水位。
|
|
34
34
|
*/
|
|
@@ -50,7 +50,7 @@ export declare class BashProcessRunner implements BashRunner {
|
|
|
50
50
|
cwd: string;
|
|
51
51
|
}): AsyncGenerator<BashOutputEvent>;
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* called by the process-level exit dispatcher on SIGINT/SIGTERM/exit.
|
|
54
54
|
* Kills the whole process group (leader + background children) via
|
|
55
55
|
* Terminator, matching agent-runner cleanupOnExit semantics.
|
|
56
56
|
*/
|
|
@@ -17,7 +17,7 @@ import { registerExitCleanup, unregisterExitCleanup } from '../common/spawning-r
|
|
|
17
17
|
export class BashProcessRunner {
|
|
18
18
|
currentProcess = null;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* 背压高水位/低水位(层④,参照 jsonl-stream.ts 的 pauseThreshold/
|
|
21
21
|
* resumeThreshold)。stdout/stderr 队列超过高水位时 pause 流(内存有界),
|
|
22
22
|
* 消费者 drain 到低水位后 resume。构造可注入以便测试用小水位。
|
|
23
23
|
*/
|
|
@@ -32,7 +32,7 @@ export class BashProcessRunner {
|
|
|
32
32
|
if (this.queueLowWater > this.queueHighWater) {
|
|
33
33
|
this.queueLowWater = this.queueHighWater;
|
|
34
34
|
}
|
|
35
|
-
// `!` bash
|
|
35
|
+
// `!` bash 无协议停止通道:posix 走组杀(与原 ProcessStopper 同实现),
|
|
36
36
|
// win32 优雅段显式跳过后树杀。
|
|
37
37
|
this.terminator = opts?.terminator ?? createTerminator({ graceMs: stopGraceMs, agent: 'bash' });
|
|
38
38
|
this.shell = opts?.shell ?? createShellBackend();
|
|
@@ -61,7 +61,7 @@ export class BashProcessRunner {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
catch (err) {
|
|
64
|
-
// win32 Git Bash
|
|
64
|
+
// win32 Git Bash 缺失:同步抛出,转成明确错误输出而非悬死。
|
|
65
65
|
if (err instanceof ShellUnavailableError) {
|
|
66
66
|
getLogger().warn(`[bash-runner] shell unavailable: ${err.message}`);
|
|
67
67
|
yield { type: 'stderr', content: `${err.message}\n` };
|
|
@@ -76,7 +76,7 @@ export class BashProcessRunner {
|
|
|
76
76
|
const stdout = proc.stdout;
|
|
77
77
|
const stderr = proc.stderr;
|
|
78
78
|
getLogger().info(`[bash-runner] spawn pid=${proc.pid} command="${command.slice(0, 50)}..." cwd=${opts.cwd}`);
|
|
79
|
-
//
|
|
79
|
+
// register with the process-level exit dispatcher while the process
|
|
80
80
|
// is alive — bridge exit/restart (SIGINT/SIGTERM/exit) then kills the whole
|
|
81
81
|
// group via cleanupOnExit instead of orphaning `!` processes.
|
|
82
82
|
registerExitCleanup(this);
|
|
@@ -85,7 +85,7 @@ export class BashProcessRunner {
|
|
|
85
85
|
// for logging, one for queueing) -- both fired on the same chunk.
|
|
86
86
|
const stdoutQueue = [];
|
|
87
87
|
const stderrQueue = [];
|
|
88
|
-
//
|
|
88
|
+
// 背压状态(层④):队列超高水位 → pause 对应流;drain 到低水位 → resume。
|
|
89
89
|
let stdoutPaused = false;
|
|
90
90
|
let stderrPaused = false;
|
|
91
91
|
const maybePauseStdout = () => {
|
|
@@ -209,7 +209,7 @@ export class BashProcessRunner {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
/**
|
|
212
|
-
*
|
|
212
|
+
* called by the process-level exit dispatcher on SIGINT/SIGTERM/exit.
|
|
213
213
|
* Kills the whole process group (leader + background children) via
|
|
214
214
|
* Terminator, matching agent-runner cleanupOnExit semantics.
|
|
215
215
|
*/
|
|
@@ -22,7 +22,7 @@ export declare class ClaudeRunner implements AgentRunner {
|
|
|
22
22
|
settings?: string;
|
|
23
23
|
stopGraceMs?: number;
|
|
24
24
|
pidDir?: string;
|
|
25
|
-
|
|
25
|
+
cwd: string;
|
|
26
26
|
spawnHeartbeatMs?: number;
|
|
27
27
|
sessionReader?: AgentSessionReader;
|
|
28
28
|
/** Claude 权限模式(官方 --permission-mode 枚举;'default'=省略参数)。 */
|
|
@@ -44,12 +44,11 @@ export class ClaudeRunner {
|
|
|
44
44
|
this.sessionReader = opts.sessionReader ?? new ClaudeSessionReader();
|
|
45
45
|
this.session = new ClaudeSession({
|
|
46
46
|
pidDir: opts.pidDir,
|
|
47
|
-
|
|
47
|
+
cwd: opts.cwd,
|
|
48
48
|
stopGraceMs: opts.stopGraceMs,
|
|
49
49
|
spawnHeartbeatMs: opts.spawnHeartbeatMs,
|
|
50
50
|
permissionMode: this.permissionMode,
|
|
51
51
|
settings: opts.settings,
|
|
52
|
-
model: this.defaultModel,
|
|
53
52
|
effort: this.defaultEffort,
|
|
54
53
|
idleTtlMs: opts.idleTtlMs,
|
|
55
54
|
});
|
|
@@ -128,7 +127,7 @@ export class ClaudeRunner {
|
|
|
128
127
|
await this.session.respondPermission(requestId, { behavior: 'deny' });
|
|
129
128
|
return;
|
|
130
129
|
case 'cancel': {
|
|
131
|
-
// 审批超时/取消:deny 送达后中断 turn
|
|
130
|
+
// 审批超时/取消:deny 送达后中断 turn(对齐「审批超时未响应,
|
|
132
131
|
// 已自动取消」终态;仅 deny 会让 claude 继续回合导致卡片 done)。
|
|
133
132
|
await this.session.respondPermission(requestId, {
|
|
134
133
|
behavior: 'deny',
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* - result 事件 subtype 为 compact/compaction 是 turn 中途压缩,不是 turn 结束;
|
|
23
23
|
* 其余 subtype(success/error/...)才是 turn 终态。
|
|
24
24
|
*
|
|
25
|
-
* 生命周期:一个
|
|
25
|
+
* 生命周期:一个 cwd 一个长驻进程(lifetime='workspace')。每次
|
|
26
26
|
* run(message) = 写一条 user 消息 + 消费 stdout 事件直到本 turn 的 result;
|
|
27
27
|
* 进程在 turn 之间保持存活(stdin 不关闭),/stop / /new / /cd / 看门狗超时
|
|
28
28
|
* 时经 Terminator 组杀,下条消息按 SessionStore 的 sessionId --resume。
|
|
@@ -31,13 +31,12 @@ import { SpawningRunner } from '../common/spawning-runner.js';
|
|
|
31
31
|
import type { AgentEvent, SpawnOptions } from '../types.js';
|
|
32
32
|
export interface ClaudeSessionOptions {
|
|
33
33
|
pidDir?: string;
|
|
34
|
-
|
|
34
|
+
cwd: string;
|
|
35
35
|
stopGraceMs?: number;
|
|
36
36
|
spawnHeartbeatMs?: number;
|
|
37
37
|
/** Claude 权限模式(官方 --permission-mode 枚举;'default' 省略该参数)。 */
|
|
38
38
|
permissionMode?: string;
|
|
39
39
|
settings?: string;
|
|
40
|
-
model?: string;
|
|
41
40
|
effort?: string;
|
|
42
41
|
/** 会话级空闲回收 TTL(ms):turn 之间无活动超过该窗口则停止进程。0=禁用。 */
|
|
43
42
|
idleTtlMs?: number;
|
|
@@ -54,14 +53,13 @@ export interface PermissionResult {
|
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
55
|
* ClaudeSession extends SpawningRunner 复用其 spawn/pid 文件/killOrphan/
|
|
57
|
-
* Terminator/SpawnHeartbeat
|
|
56
|
+
* Terminator/SpawnHeartbeat/退出分发器机制(契约保持),
|
|
58
57
|
* 但 run() 覆盖为「长驻 + 按 turn 消费」:进程跨 turn 存活,turn 结束以
|
|
59
58
|
* stream-json result 事件(非 compact)为界,而不是进程退出。
|
|
60
59
|
*/
|
|
61
60
|
export declare class ClaudeSession extends SpawningRunner {
|
|
62
61
|
private readonly permissionMode;
|
|
63
62
|
private readonly settings?;
|
|
64
|
-
private readonly defaultModel?;
|
|
65
63
|
private readonly defaultEffort?;
|
|
66
64
|
private readonly idleTtlMs;
|
|
67
65
|
/** 当前 turn 是否在途(防止并发 run 写乱 stdin)。 */
|
|
@@ -140,7 +138,7 @@ export declare class ClaudeSession extends SpawningRunner {
|
|
|
140
138
|
private startStreamReader;
|
|
141
139
|
/**
|
|
142
140
|
* 消费事件队列直到本 turn 的 result 事件(非 compact/compaction)。
|
|
143
|
-
* 消费者提前关闭生成器(bridge 循环体抛错)→
|
|
141
|
+
* 消费者提前关闭生成器(bridge 循环体抛错)→ 杀进程防孤儿(语义)。
|
|
144
142
|
*
|
|
145
143
|
* result = turn 终态(2026-08-16 真实 claude 实测:长驻模式下 result 之后
|
|
146
144
|
* 不再有 stdout 事件,后台任务完成是静默的;旧 -p 流程「等进程退出再收尾」
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
* - result 事件 subtype 为 compact/compaction 是 turn 中途压缩,不是 turn 结束;
|
|
23
23
|
* 其余 subtype(success/error/...)才是 turn 终态。
|
|
24
24
|
*
|
|
25
|
-
* 生命周期:一个
|
|
25
|
+
* 生命周期:一个 cwd 一个长驻进程(lifetime='workspace')。每次
|
|
26
26
|
* run(message) = 写一条 user 消息 + 消费 stdout 事件直到本 turn 的 result;
|
|
27
27
|
* 进程在 turn 之间保持存活(stdin 不关闭),/stop / /new / /cd / 看门狗超时
|
|
28
28
|
* 时经 Terminator 组杀,下条消息按 SessionStore 的 sessionId --resume。
|
|
29
29
|
*/
|
|
30
30
|
import { readFile } from 'node:fs/promises';
|
|
31
|
-
import {
|
|
31
|
+
import { bestEffortUnlink } from '../../common/fs.js';
|
|
32
32
|
import { getLogger } from '../../logger/index.js';
|
|
33
33
|
import { SpawningRunner } from '../common/spawning-runner.js';
|
|
34
|
-
import {
|
|
34
|
+
import { setupErrorEvent, syntheticInitEvent } from '../common/synthetic-events.js';
|
|
35
35
|
import { makeQuestionApprovalEvent } from '../question-common.js';
|
|
36
36
|
/** 拒绝权限时的默认提示(进入 claude 上下文,中文用户可读)。 */
|
|
37
37
|
const DENY_MESSAGE = '用户拒绝了此工具调用,请停止并等待用户指令。';
|
|
@@ -43,14 +43,13 @@ const DEFAULT_IDLE_TTL_MS = 30 * 60 * 1000;
|
|
|
43
43
|
const COMPACTION_RESULT_SUBTYPES = new Set(['compact', 'compaction']);
|
|
44
44
|
/**
|
|
45
45
|
* ClaudeSession extends SpawningRunner 复用其 spawn/pid 文件/killOrphan/
|
|
46
|
-
* Terminator/SpawnHeartbeat
|
|
46
|
+
* Terminator/SpawnHeartbeat/退出分发器机制(契约保持),
|
|
47
47
|
* 但 run() 覆盖为「长驻 + 按 turn 消费」:进程跨 turn 存活,turn 结束以
|
|
48
48
|
* stream-json result 事件(非 compact)为界,而不是进程退出。
|
|
49
49
|
*/
|
|
50
50
|
export class ClaudeSession extends SpawningRunner {
|
|
51
51
|
permissionMode;
|
|
52
52
|
settings;
|
|
53
|
-
defaultModel;
|
|
54
53
|
defaultEffort;
|
|
55
54
|
idleTtlMs;
|
|
56
55
|
/** 当前 turn 是否在途(防止并发 run 写乱 stdin)。 */
|
|
@@ -92,7 +91,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
92
91
|
constructor(opts) {
|
|
93
92
|
super({
|
|
94
93
|
pidDir: opts.pidDir,
|
|
95
|
-
|
|
94
|
+
cwd: opts.cwd,
|
|
96
95
|
stopGraceMs: opts.stopGraceMs,
|
|
97
96
|
spawnHeartbeatMs: opts.spawnHeartbeatMs,
|
|
98
97
|
pidFilePrefix: 'claude',
|
|
@@ -103,7 +102,6 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
103
102
|
this.binary = 'claude';
|
|
104
103
|
this.permissionMode = opts.permissionMode ?? 'bypassPermissions';
|
|
105
104
|
this.settings = opts.settings;
|
|
106
|
-
this.defaultModel = opts.model;
|
|
107
105
|
this.defaultEffort = opts.effort;
|
|
108
106
|
this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
|
|
109
107
|
}
|
|
@@ -129,7 +127,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
129
127
|
// 或 --resume 目标会话)。比较进程当前 session(init 报告值)而非 spawn
|
|
130
128
|
// 请求值:bridge 会把 init 的 session_id 写回 SessionStore,第二条消息
|
|
131
129
|
// 带着它来;若拿 spawn 请求值('')比较,每条消息都会误判切换、杀进程
|
|
132
|
-
// 重启,长驻设计失效(2026-08-16
|
|
130
|
+
// 重启,长驻设计失效(2026-08-16)。
|
|
133
131
|
if (this.isRunning && this.sessionId !== (opts.sessionId ?? '')) {
|
|
134
132
|
getLogger().info(`[${this.logTag}] session mismatch: process=${this.sessionId || '(fresh)'} ` +
|
|
135
133
|
`requested=${opts.sessionId ?? '(fresh)'}, recycling process`);
|
|
@@ -141,7 +139,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
141
139
|
// §9.22 守卫:错误 result 前必须补 synthetic init,否则 bridge 的
|
|
142
140
|
// pre-init result guard 与 run-state reducer 会静默丢弃错误信息。
|
|
143
141
|
yield syntheticInitEvent(opts.sessionId);
|
|
144
|
-
yield
|
|
142
|
+
yield setupErrorEvent(spawnError);
|
|
145
143
|
return;
|
|
146
144
|
}
|
|
147
145
|
// turn 之间的 idle 噪音(如 prompt_suggestion)不属于本 turn,先清空。
|
|
@@ -176,7 +174,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
176
174
|
getLogger().warn(`[${this.logTag}] respondPermission skipped: process not running requestId=${requestId}`);
|
|
177
175
|
return;
|
|
178
176
|
}
|
|
179
|
-
//
|
|
177
|
+
// requestId 不在待审批表(已 control_cancel / 进程回收)时
|
|
180
178
|
// 丢弃响应,不能以空 updatedInput allow(claude 会拿空输入执行工具)。
|
|
181
179
|
if (!this.pendingToolInputs.has(requestId)) {
|
|
182
180
|
getLogger().warn(`[${this.logTag}] respondPermission dropped: unknown requestId=${requestId}`);
|
|
@@ -206,7 +204,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
206
204
|
async respondAskUserQuestion(requestId, answers) {
|
|
207
205
|
if (!this.isRunning)
|
|
208
206
|
return;
|
|
209
|
-
//
|
|
207
|
+
// 未知 requestId 时不能回写缺原始 questions 的 answers
|
|
210
208
|
// (官方要求 updatedInput 必须包含原始 questions)。
|
|
211
209
|
if (!this.pendingToolInputs.has(requestId)) {
|
|
212
210
|
getLogger().warn(`[${this.logTag}] respondAskUserQuestion dropped: unknown requestId=${requestId}`);
|
|
@@ -287,7 +285,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
287
285
|
*/
|
|
288
286
|
async startProcess(opts) {
|
|
289
287
|
if (this.isRunning) {
|
|
290
|
-
//
|
|
288
|
+
// 复用进程(跨 turn 长驻)时清掉上一轮的空闲回收 timer——
|
|
291
289
|
// 否则长 turn 中它会空转触发一次(回调因 turnActive 空转返回,无害但
|
|
292
290
|
// 多余);新一轮结束后的 armIdleTimer 会重新武装。
|
|
293
291
|
this.clearIdleTimer();
|
|
@@ -372,7 +370,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
372
370
|
// 进程自行结束(崩溃/上游退出):注销协议停止通道,别把死 pid 的
|
|
373
371
|
// 条目留在注册表里等 pid 复用。
|
|
374
372
|
this.unregisterStopper();
|
|
375
|
-
|
|
373
|
+
bestEffortUnlink(this.pidFilePath);
|
|
376
374
|
this.wakeWaiters();
|
|
377
375
|
}
|
|
378
376
|
}
|
|
@@ -383,7 +381,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
383
381
|
// =========================================================================
|
|
384
382
|
/**
|
|
385
383
|
* 消费事件队列直到本 turn 的 result 事件(非 compact/compaction)。
|
|
386
|
-
* 消费者提前关闭生成器(bridge 循环体抛错)→
|
|
384
|
+
* 消费者提前关闭生成器(bridge 循环体抛错)→ 杀进程防孤儿(语义)。
|
|
387
385
|
*
|
|
388
386
|
* result = turn 终态(2026-08-16 真实 claude 实测:长驻模式下 result 之后
|
|
389
387
|
* 不再有 stdout 事件,后台任务完成是静默的;旧 -p 流程「等进程退出再收尾」
|
|
@@ -482,7 +480,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
482
480
|
this.idleTimer = null;
|
|
483
481
|
if (this.turnActive || !this.isRunning)
|
|
484
482
|
return;
|
|
485
|
-
getLogger().info(`[${this.logTag}] idle timeout ${this.idleTtlMs}ms, stopping session
|
|
483
|
+
getLogger().info(`[${this.logTag}] idle timeout ${this.idleTtlMs}ms, stopping session cwd=${this.cwd}`);
|
|
486
484
|
void this.stop().catch((err) => {
|
|
487
485
|
getLogger().warn(`[${this.logTag}] idle stop failed: ${err.message}`);
|
|
488
486
|
});
|
|
@@ -556,7 +554,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
556
554
|
async translateEvent(raw) {
|
|
557
555
|
const event = raw;
|
|
558
556
|
const type = event.type;
|
|
559
|
-
//
|
|
557
|
+
// --resume 会先重放上一轮历史事件(旧 result 之外还可能有
|
|
560
558
|
// 旧 assistant/user 内容)。init 之前除 init 本身外一律丢弃,避免历史
|
|
561
559
|
// 内容混进当前卡片(result 分支的 sawInit 守卫升级为全类型守卫)。
|
|
562
560
|
if (type !== 'system' && !this.sawInit)
|
|
@@ -680,7 +678,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
680
678
|
const request = (raw.request ?? {});
|
|
681
679
|
const subtype = request.subtype;
|
|
682
680
|
if (subtype !== 'can_use_tool') {
|
|
683
|
-
//
|
|
681
|
+
// 未知 subtype 不能静默丢弃——claude 在等 control_response,
|
|
684
682
|
// 不回应 turn 会永久挂起(无审批卡、无超时)。deny 兜底让 claude 继续。
|
|
685
683
|
getLogger().warn(`[${this.logTag}] unknown control request subtype=${String(subtype)} requestId=${requestId}`);
|
|
686
684
|
void this.writeControlResponse(requestId, {
|
|
@@ -694,7 +692,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
694
692
|
const toolName = String(request.tool_name ?? '');
|
|
695
693
|
const input = (request.input ?? {});
|
|
696
694
|
this.pendingToolInputs.set(requestId, input);
|
|
697
|
-
//
|
|
695
|
+
// 允许所有只放行工具权限;AskUserQuestion 不能被空 answers
|
|
698
696
|
// 自动放行(claude 会把空答案当作已作答),必须继续上抛卡片让用户回答。
|
|
699
697
|
if (this.autoApprove && toolName !== 'AskUserQuestion') {
|
|
700
698
|
getLogger().info(`[${this.logTag}] auto-approving requestId=${requestId} tool=${toolName}`);
|
|
@@ -704,7 +702,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
704
702
|
}).catch((err) => {
|
|
705
703
|
getLogger().warn(`[${this.logTag}] auto-approve write failed requestId=${requestId}: ${err.message}`);
|
|
706
704
|
});
|
|
707
|
-
//
|
|
705
|
+
// 自动放行不经过协调器/respondPermission,条目不会随
|
|
708
706
|
// 响应被删除——立即释放,否则长会话(允许所有)下 Map 无界增长。
|
|
709
707
|
this.pendingToolInputs.delete(requestId);
|
|
710
708
|
return [];
|
|
@@ -719,7 +717,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
719
717
|
behavior: 'deny',
|
|
720
718
|
message: '无法解析 AskUserQuestion 选项',
|
|
721
719
|
}).catch(() => { });
|
|
722
|
-
//
|
|
720
|
+
// 解析失败 deny 后同样释放条目(不经过 respondPermission)。
|
|
723
721
|
this.pendingToolInputs.delete(requestId);
|
|
724
722
|
return [];
|
|
725
723
|
}
|
|
@@ -762,7 +760,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
762
760
|
];
|
|
763
761
|
}
|
|
764
762
|
// 非命令工具的兜底防御:input 无 command 字段时 JSON 序列化,但空对象
|
|
765
|
-
// 序列化成 `{}`
|
|
763
|
+
// 序列化成 `{}` 对用户无意义——内容为空时回退展示工具名。
|
|
766
764
|
const rawCommand = typeof input.command === 'string' ? input.command : JSON.stringify(input);
|
|
767
765
|
const command = rawCommand && rawCommand !== '{}' && rawCommand !== 'null'
|
|
768
766
|
? rawCommand
|
|
@@ -819,7 +817,7 @@ function parseUserQuestions(input) {
|
|
|
819
817
|
const question = typeof q.question === 'string' ? q.question : '';
|
|
820
818
|
if (!question)
|
|
821
819
|
continue;
|
|
822
|
-
//
|
|
820
|
+
// answers 字典以问题文本为 key,重复文本会互相覆盖——
|
|
823
821
|
// 视为解析失败(调用方 deny 兜底),不把坏问题上抛给用户。
|
|
824
822
|
if (seenQuestions.has(question))
|
|
825
823
|
return [];
|
|
@@ -841,7 +839,7 @@ function parseUserQuestions(input) {
|
|
|
841
839
|
});
|
|
842
840
|
}
|
|
843
841
|
}
|
|
844
|
-
//
|
|
842
|
+
// 零选项问题无法作答(多选连自定义答案都没有),视为
|
|
845
843
|
// 解析失败(调用方 deny 兜底),不把坏问题上抛给用户。
|
|
846
844
|
if (options.length === 0)
|
|
847
845
|
return [];
|
|
@@ -136,7 +136,7 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
136
136
|
},
|
|
137
137
|
};
|
|
138
138
|
this.connectionManager = new ConnectionManager(managerOpts);
|
|
139
|
-
//
|
|
139
|
+
// 协议停止通道:codex 的 cancel 是 `turn/interrupt` 请求,已由
|
|
140
140
|
// cancelCurrentTurn 实现;登记后 win32 优雅停止才有通道可用(否则一路树杀)。
|
|
141
141
|
this.connectionManager.stopper = ({ client }) => this.buildCooperativeStop(client);
|
|
142
142
|
}
|
|
@@ -235,7 +235,7 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
235
235
|
else if (pending.kind === 'permissions') {
|
|
236
236
|
// 权限审批的响应没有 decision 字段(真实协议只回 granted profile +
|
|
237
237
|
// scope):decline/cancel 必须返回空授权(拒绝全部),否则会把用户已
|
|
238
|
-
// 勾选的条目当作授予返回(2026-08-12
|
|
238
|
+
// 勾选的条目当作授予返回(2026-08-12 :勾选后点「拒绝」实
|
|
239
239
|
// 际授予了所选权限)。accept/acceptForSession 才带用户勾选的条目。
|
|
240
240
|
const denied = action === 'decline' || action === 'cancel';
|
|
241
241
|
client.respond(requestId, {
|
|
@@ -358,7 +358,7 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
358
358
|
}
|
|
359
359
|
else {
|
|
360
360
|
const threadResult = await client.request('thread/start', threadParams);
|
|
361
|
-
//
|
|
361
|
+
// 会话键假设:主线程 thread.id === session_meta.session_id,
|
|
362
362
|
// 故 thread.id 可同时用作协议 threadId 与 store/session reader 的 session
|
|
363
363
|
// 键(bridge 用 turn_started 通知的 threadId 写回,/resume、Compact 按此
|
|
364
364
|
// 键定位 JSONL)。forked/subagent 线程二者会分叉(openai/codex#29327),
|
|
@@ -26,7 +26,7 @@ export declare class CodexAppServerTranslator {
|
|
|
26
26
|
/** Full command output per item id (item-scoped snapshot). */
|
|
27
27
|
private toolOutputByItem;
|
|
28
28
|
/**
|
|
29
|
-
* Tool identity per item id (信息保真
|
|
29
|
+
* Tool identity per item id (信息保真): recorded at item/started, spread
|
|
30
30
|
* into every toolOutput turn_diff, released at item/completed.
|
|
31
31
|
*/
|
|
32
32
|
private toolItemMeta;
|
|
@@ -85,7 +85,7 @@ export declare class CodexAppServerTranslator {
|
|
|
85
85
|
private handlePlanUpdated;
|
|
86
86
|
private handleTokenUsageUpdated;
|
|
87
87
|
/**
|
|
88
|
-
* warning 通知 → notice 事件(信息保真
|
|
88
|
+
* warning 通知 → notice 事件(信息保真 运行期告警不再静默丢弃)。
|
|
89
89
|
*/
|
|
90
90
|
private handleWarning;
|
|
91
91
|
/**
|
|
@@ -27,7 +27,7 @@ export class CodexAppServerTranslator {
|
|
|
27
27
|
/** Full command output per item id (item-scoped snapshot). */
|
|
28
28
|
toolOutputByItem = new Map();
|
|
29
29
|
/**
|
|
30
|
-
* Tool identity per item id (信息保真
|
|
30
|
+
* Tool identity per item id (信息保真): recorded at item/started, spread
|
|
31
31
|
* into every toolOutput turn_diff, released at item/completed.
|
|
32
32
|
*/
|
|
33
33
|
toolItemMeta = new Map();
|
|
@@ -326,7 +326,7 @@ export class CodexAppServerTranslator {
|
|
|
326
326
|
return [];
|
|
327
327
|
}
|
|
328
328
|
this.toolOutputByItem.set(item.id, authoritative);
|
|
329
|
-
// 信息保真
|
|
329
|
+
// 信息保真 完成事件同样携带身份,并在发出后释放 meta。
|
|
330
330
|
const meta = this.toolItemMeta.get(item.id);
|
|
331
331
|
this.toolItemMeta.delete(item.id);
|
|
332
332
|
return [
|
|
@@ -383,7 +383,7 @@ export class CodexAppServerTranslator {
|
|
|
383
383
|
];
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
|
-
// 信息保真
|
|
386
|
+
// 信息保真 webSearch/mcpToolCall/dynamicToolCall 等无专属 completed 分支的
|
|
387
387
|
// 工具 item 也必须收尾——否则 run-state 侧工具块永挂 running(无 completedAt/终态),
|
|
388
388
|
// toolItemMeta 泄漏到下个 turn 的 clear() 才被释放。
|
|
389
389
|
if (this.startedToolItems.has(item.id)) {
|
|
@@ -466,7 +466,7 @@ export class CodexAppServerTranslator {
|
|
|
466
466
|
return [];
|
|
467
467
|
}
|
|
468
468
|
/**
|
|
469
|
-
* warning 通知 → notice 事件(信息保真
|
|
469
|
+
* warning 通知 → notice 事件(信息保真 运行期告警不再静默丢弃)。
|
|
470
470
|
*/
|
|
471
471
|
handleWarning(params) {
|
|
472
472
|
return [
|
|
@@ -653,7 +653,7 @@ export class CodexAppServerTranslator {
|
|
|
653
653
|
// item/started 锚定工具块位置(真实时序):命令开始时即创建工具块,
|
|
654
654
|
// 而不是等到首个输出 delta——否则后续 reasoning item 会插到 command 之前。
|
|
655
655
|
this.startedToolItems.add(item.id);
|
|
656
|
-
// 信息保真
|
|
656
|
+
// 信息保真 记录工具身份,随 turn_diff 透传(run-state 首见时写入)。
|
|
657
657
|
this.toolItemMeta.set(item.id, {
|
|
658
658
|
toolName: 'Bash',
|
|
659
659
|
toolInput: { command: item.command, ...(item.cwd ? { cwd: item.cwd } : {}) },
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ACP approval/permission mapping and lifecycle shared by the kimi and
|
|
3
|
+
* opencode ACP runners.
|
|
3
4
|
*
|
|
4
|
-
* Extracted from runner.ts and translator.ts to avoid duplication
|
|
5
|
+
* Extracted from runner.ts and translator.ts to avoid duplication.
|
|
5
6
|
* Shape source: approval.ts:28-29 — optionId is opaque, echo back as-is.
|
|
6
7
|
*/
|
|
7
8
|
import type { ApprovalView } from '../../types.js';
|
|
8
9
|
import type { PermissionOption } from './protocol-types.js';
|
|
9
|
-
/**
|
|
10
|
-
* Find an option by matching its `kind` against a list of candidate values,
|
|
11
|
-
* and return the `optionId` to echo back in the approval response.
|
|
12
|
-
* ACP option kind values vary between kimi versions; this handles both
|
|
13
|
-
* the documented names and the observed real names (§2.3).
|
|
14
|
-
*/
|
|
15
|
-
export declare function findOptionIdByKind(options: PermissionOption[], candidateKinds: string[]): string | undefined;
|
|
16
10
|
/** 各 ACP 服务端的 option kind 候选表(kimi/opencode 观测名不同,语义对齐)。 */
|
|
17
11
|
export interface AcpApprovalKinds {
|
|
18
12
|
accept: string[];
|
|
@@ -39,7 +33,7 @@ export declare function buildAcpPermissionOutcome(action: string, options: Permi
|
|
|
39
33
|
/**
|
|
40
34
|
* 从服务端 options kind 派生审批决定列表:accept/decline/cancel 恒有;
|
|
41
35
|
* 带 always 类 option(kimi approve_always / opencode allow_always)才提供
|
|
42
|
-
* acceptForSession
|
|
36
|
+
* acceptForSession(「本会话总是允许」)。kimi/opencode 两侧共用。
|
|
43
37
|
*/
|
|
44
38
|
export declare function deriveAcpAvailableDecisions(options: PermissionOption[]): string[];
|
|
45
39
|
/** 截断长文本并追加省略号(translator 审批 reason 用,原先两份相同拷贝)。 */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ACP approval/permission mapping and lifecycle shared by the kimi and
|
|
3
|
+
* opencode ACP runners.
|
|
3
4
|
*
|
|
4
|
-
* Extracted from runner.ts and translator.ts to avoid duplication
|
|
5
|
+
* Extracted from runner.ts and translator.ts to avoid duplication.
|
|
5
6
|
* Shape source: approval.ts:28-29 — optionId is opaque, echo back as-is.
|
|
6
7
|
*/
|
|
7
8
|
import { getLogger } from '../../../logger/index.js';
|
|
@@ -9,9 +10,9 @@ import { getLogger } from '../../../logger/index.js';
|
|
|
9
10
|
* Find an option by matching its `kind` against a list of candidate values,
|
|
10
11
|
* and return the `optionId` to echo back in the approval response.
|
|
11
12
|
* ACP option kind values vary between kimi versions; this handles both
|
|
12
|
-
* the documented names and the observed real names
|
|
13
|
+
* the documented names and the observed real names.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
+
function findOptionIdByKind(options, candidateKinds) {
|
|
15
16
|
for (const kind of candidateKinds) {
|
|
16
17
|
const found = options.find((opt) => opt.kind === kind);
|
|
17
18
|
if (found)
|
|
@@ -53,7 +54,7 @@ export function buildAcpPermissionOutcome(action, options, kinds) {
|
|
|
53
54
|
/**
|
|
54
55
|
* 从服务端 options kind 派生审批决定列表:accept/decline/cancel 恒有;
|
|
55
56
|
* 带 always 类 option(kimi approve_always / opencode allow_always)才提供
|
|
56
|
-
* acceptForSession
|
|
57
|
+
* acceptForSession(「本会话总是允许」)。kimi/opencode 两侧共用。
|
|
57
58
|
*/
|
|
58
59
|
export function deriveAcpAvailableDecisions(options) {
|
|
59
60
|
const decisions = ['accept', 'decline', 'cancel'];
|
|
@@ -100,4 +101,4 @@ export async function sendAcpSetMode(opts) {
|
|
|
100
101
|
getLogger().warn(`[${opts.logTag}] session/set_mode failed (non-fatal): ${err.message}`);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
|
-
//# sourceMappingURL=
|
|
104
|
+
//# sourceMappingURL=acp-approval.js.map
|
|
@@ -22,7 +22,7 @@ import type { AgentEvent, AgentKind, AgentSessionReader, SpawnOptions } from '..
|
|
|
22
22
|
import { ConnectionManager } from '../jsonrpc/connection-manager.js';
|
|
23
23
|
import { JsonRpcClient } from '../jsonrpc/client.js';
|
|
24
24
|
import { type SessionNewResult, type SessionResumeResult, type SessionPromptResult, type RequestPermissionParams } from './protocol-types.js';
|
|
25
|
-
import { type AcpPendingApproval } from './
|
|
25
|
+
import { type AcpPendingApproval } from './acp-approval.js';
|
|
26
26
|
import { ConnectionBasedRunner } from '../connection-based-runner.js';
|
|
27
27
|
import { BaseAcpTranslator, type AcpTranslatorEvent } from './base-translator.js';
|
|
28
28
|
export type { AcpTranslatorEvent } from './base-translator.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { respondAcpApproval, sendAcpSetMode, truncateWithEllipsis, } from './
|
|
1
|
+
import { respondAcpApproval, sendAcpSetMode, truncateWithEllipsis, } from './acp-approval.js';
|
|
2
2
|
import { getLogger } from '../../../logger/index.js';
|
|
3
3
|
import { ConnectionBasedRunner } from '../connection-based-runner.js';
|
|
4
4
|
export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
@@ -15,7 +15,7 @@ export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
|
15
15
|
constructor(opts, connectionManager) {
|
|
16
16
|
super(opts);
|
|
17
17
|
this.connectionManager = connectionManager;
|
|
18
|
-
//
|
|
18
|
+
// 协议停止通道:ACP 的 cancel 就是 `session/cancel`,已由
|
|
19
19
|
// cancelCurrentTurn 实现。这里只做登记接线——manager 建在 super() 实参里,
|
|
20
20
|
// 那时 this 不可用,所以只能等 super() 返回后再赋值。
|
|
21
21
|
connectionManager.stopper = ({ client }) => this.buildCooperativeStop(client);
|