lark-remote 0.4.4 → 0.4.5
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 +10 -0
- package/dist/binder.d.ts +11 -10
- package/dist/binder.js +17 -16
- package/dist/bridge/inbound-media.d.ts +1 -27
- package/dist/bridge/inbound-media.js +13 -14
- package/dist/bridge/index.d.ts +49 -49
- package/dist/bridge/index.js +99 -92
- package/dist/bridge/queue-manager.d.ts +33 -26
- package/dist/bridge/queue-manager.js +117 -117
- package/dist/card/bash-renderer.js +4 -4
- package/dist/card/card-shared.d.ts +1 -1
- package/dist/card/card-shared.js +2 -2
- package/dist/card/run-renderer.js +8 -8
- package/dist/cli.js +5 -6
- package/dist/clone.d.ts +1 -6
- package/dist/clone.js +4 -4
- 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/config/dir.d.ts +4 -5
- package/dist/config/dir.js +7 -8
- package/dist/config/wizard.js +2 -2
- package/dist/connector/index.d.ts +0 -2
- package/dist/connector/index.js +5 -5
- 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 +30 -22
- package/dist/instance-lock.js +4 -4
- package/dist/persistence/atomic-write.js +3 -3
- package/dist/platform/agent-stopper.d.ts +1 -1
- package/dist/platform/terminator-posix.js +5 -5
- package/dist/platform/terminator-win32.js +1 -1
- package/dist/router/card-helpers.d.ts +1 -3
- package/dist/router/card-helpers.js +2 -2
- package/dist/router/config/codex.js +1 -1
- 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 +1 -1
- package/dist/router/config/pi.js +1 -1
- package/dist/router/index.d.ts +22 -22
- package/dist/router/index.js +71 -52
- package/dist/router/order-exec-dispatch.d.ts +3 -10
- package/dist/router/order-exec-dispatch.js +3 -3
- 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/claude/runner.d.ts +1 -1
- package/dist/runner/claude/runner.js +1 -2
- package/dist/runner/claude/session.d.ts +2 -4
- package/dist/runner/claude/session.js +7 -9
- package/dist/runner/common/acp/{protocol-helpers.d.ts → acp-approval.d.ts} +2 -8
- package/dist/runner/common/acp/{protocol-helpers.js → acp-approval.js} +4 -3
- package/dist/runner/common/acp/base-acp-runner.d.ts +1 -1
- package/dist/runner/common/acp/base-acp-runner.js +1 -1
- package/dist/runner/common/connection-based-runner.d.ts +4 -4
- package/dist/runner/common/connection-based-runner.js +3 -3
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +12 -12
- package/dist/runner/common/jsonrpc/connection-manager.js +36 -36
- 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 +2 -2
- package/dist/runner/common/spawning-runner.js +8 -8
- 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 +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.js +1 -1
- package/dist/runner/kimi/acp/translator.js +1 -1
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +1 -1
- package/dist/runner/opencode/acp/translator.js +1 -1
- package/dist/runner/pi/rpc/runner.d.ts +1 -1
- package/dist/runner/pi/rpc/translator.d.ts +0 -1
- package/dist/runner/pi/rpc/translator.js +0 -2
- package/dist/runner/registry.d.ts +3 -3
- package/dist/runner/registry.js +3 -3
- package/dist/session/claude/session-index.d.ts +1 -1
- package/dist/session/claude/session-index.js +1 -1
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/session-reader.js +1 -1
- package/dist/session/codex/rollout-reader.js +2 -2
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +1 -1
- package/dist/session/kimi/sessions.d.ts +0 -4
- package/dist/session/kimi/sessions.js +2 -8
- package/dist/session/opencode/sessions.js +3 -3
- package/dist/session/registry.d.ts +1 -1
- package/dist/session/registry.js +1 -1
- package/dist/session/session-store.d.ts +2 -3
- package/dist/session/session-store.js +2 -4
- 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,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ACP approval/permission mapping and lifecycle shared by the kimi and
|
|
3
|
+
* opencode ACP runners.
|
|
3
4
|
*
|
|
4
5
|
* Extracted from runner.ts and translator.ts to avoid duplication (G5).
|
|
5
6
|
* Shape source: approval.ts:28-29 — optionId is opaque, echo back as-is.
|
|
@@ -11,7 +12,7 @@ import { getLogger } from '../../../logger/index.js';
|
|
|
11
12
|
* ACP option kind values vary between kimi versions; this handles both
|
|
12
13
|
* the documented names and the observed real names (§2.3).
|
|
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)
|
|
@@ -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 {
|
|
@@ -60,9 +60,9 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
|
|
|
60
60
|
* 两件事,顺序不能反:
|
|
61
61
|
* 1. 若本连接就是当前在途 turn 的连接,先发协议取消(codex turn/interrupt /
|
|
62
62
|
* ACP session/cancel / pi abort)。**归属判定不能省**:runner 实例按 agent
|
|
63
|
-
* 维度共享(一个实例管多条
|
|
63
|
+
* 维度共享(一个实例管多条 cwd 连接),无条件调用
|
|
64
64
|
* `cancelCurrentTurn()` 会用它自己的 currentClient/activeSessionId 去打断
|
|
65
|
-
* **另一个
|
|
65
|
+
* **另一个 cwd** 正在跑的 turn;
|
|
66
66
|
* 2. 关 stdin。stdio 型 agent server 在 stdin EOF 后自行退出——只发协议取消
|
|
67
67
|
* 不会让进程退出,优雅段会空转到 grace 超时后照样树杀,等于通道白注册。
|
|
68
68
|
*/
|
|
@@ -118,9 +118,9 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
|
|
|
118
118
|
protected respawnAfterConnectionLost(opts: SpawnOptions): Promise<void>;
|
|
119
119
|
/** Hook: clear subclass turn state in run() finally (session ids, translator, flags). */
|
|
120
120
|
protected abstract clearTurnState(): void;
|
|
121
|
-
/** Release the connection for a
|
|
121
|
+
/** Release the connection for a cwd (subclass dispatches to its manager). */
|
|
122
122
|
protected abstract releaseConnection(cwd: string): Promise<void>;
|
|
123
|
-
/** Notify the connection manager that the
|
|
123
|
+
/** Notify the connection manager that the cwd is idle (subclass). */
|
|
124
124
|
protected abstract notifyIdle(cwd: string): void;
|
|
125
125
|
/** Dispose all connections (subclass dispatches to its manager). */
|
|
126
126
|
protected abstract disposeConnections(): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConnectionLostError } from './jsonrpc/client.js';
|
|
2
2
|
import { getLogger } from '../../logger/index.js';
|
|
3
|
-
import { syntheticInitEvent } from './
|
|
3
|
+
import { syntheticInitEvent } from './synthetic-events.js';
|
|
4
4
|
import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
|
|
5
5
|
/** How long to wait for turn output notifications before failing.
|
|
6
6
|
* 30 分钟(单源于 config 的 DEFAULT_TURN_IDLE_TIMEOUT_MINUTES):ACP/app-server
|
|
@@ -37,9 +37,9 @@ export class ConnectionBasedRunner {
|
|
|
37
37
|
* 两件事,顺序不能反:
|
|
38
38
|
* 1. 若本连接就是当前在途 turn 的连接,先发协议取消(codex turn/interrupt /
|
|
39
39
|
* ACP session/cancel / pi abort)。**归属判定不能省**:runner 实例按 agent
|
|
40
|
-
* 维度共享(一个实例管多条
|
|
40
|
+
* 维度共享(一个实例管多条 cwd 连接),无条件调用
|
|
41
41
|
* `cancelCurrentTurn()` 会用它自己的 currentClient/activeSessionId 去打断
|
|
42
|
-
* **另一个
|
|
42
|
+
* **另一个 cwd** 正在跑的 turn;
|
|
43
43
|
* 2. 关 stdin。stdio 型 agent server 在 stdin EOF 后自行退出——只发协议取消
|
|
44
44
|
* 不会让进程退出,优雅段会空转到 grace 超时后照样树杀,等于通道白注册。
|
|
45
45
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ConnectionManager: manages agent-server connections per
|
|
2
|
+
* ConnectionManager: manages agent-server connections per cwd, shared by
|
|
3
3
|
* all workspace-lifetime JSON-line runners (codex app-server, kimi acp,
|
|
4
4
|
* opencode acp, and pi rpc).
|
|
5
5
|
*
|
|
6
|
-
* Each
|
|
6
|
+
* Each cwd gets its own connection (transport + client). Connections are
|
|
7
7
|
* created on demand via `acquire()`, cached for reuse, and released after an
|
|
8
8
|
* idle timeout (30 minutes default).
|
|
9
9
|
*
|
|
@@ -92,7 +92,7 @@ export declare class ConnectionManager<TClient extends ConnectionClient = JsonRp
|
|
|
92
92
|
private readonly logTag;
|
|
93
93
|
private readonly stoppers;
|
|
94
94
|
/** Callback when a connection is lost — cleared from slot map. */
|
|
95
|
-
onConnectionLost?: (
|
|
95
|
+
onConnectionLost?: (cwd: string) => void;
|
|
96
96
|
/**
|
|
97
97
|
* 协议停止通道工厂(design §3.3):连接建立后按 pid 登记到 AgentStopperRegistry,
|
|
98
98
|
* 供 Terminator 在**优雅**停止时回调(win32 上没有可拦截的 SIGTERM,只能靠
|
|
@@ -108,37 +108,37 @@ export declare class ConnectionManager<TClient extends ConnectionClient = JsonRp
|
|
|
108
108
|
}) => AgentStopper | undefined;
|
|
109
109
|
constructor(opts: ConnectionManagerOptions<TClient>);
|
|
110
110
|
/**
|
|
111
|
-
* Acquire a connection for the given
|
|
111
|
+
* Acquire a connection for the given cwd.
|
|
112
112
|
* Creates a new connection if one does not exist or the previous one was lost
|
|
113
113
|
* (or, for session-bound protocols, bound to a different session).
|
|
114
114
|
* Serializes creation so concurrent calls share the same connection.
|
|
115
115
|
*/
|
|
116
|
-
acquire(
|
|
116
|
+
acquire(cwd: string, req?: AcquireRequest): Promise<TClient>;
|
|
117
117
|
/**
|
|
118
118
|
* Record the session id a fresh connection actually bound to (e.g. discovered
|
|
119
119
|
* via a protocol `get_state` after a fresh spawn). This lets a subsequent run
|
|
120
120
|
* resume the SAME session on the live connection instead of respawning it.
|
|
121
121
|
*/
|
|
122
|
-
bindSession(
|
|
122
|
+
bindSession(cwd: string, sessionId: string): void;
|
|
123
123
|
/**
|
|
124
|
-
* Release (dispose) a connection for the given
|
|
124
|
+
* Release (dispose) a connection for the given cwd.
|
|
125
125
|
*/
|
|
126
|
-
release(
|
|
126
|
+
release(cwd: string): Promise<void>;
|
|
127
127
|
/**
|
|
128
128
|
* Dispose all connections.
|
|
129
129
|
*/
|
|
130
130
|
disposeAll(): Promise<void>;
|
|
131
131
|
/**
|
|
132
|
-
* Notify that the
|
|
132
|
+
* Notify that the cwd is active — disarm the idle timer.
|
|
133
133
|
*/
|
|
134
|
-
notifyActivity(
|
|
134
|
+
notifyActivity(cwd: string): void;
|
|
135
135
|
/**
|
|
136
|
-
* Notify that the
|
|
136
|
+
* Notify that the cwd is idle — arm the idle timer.
|
|
137
137
|
*
|
|
138
138
|
* `idleTtlMs: 0` = 不回收(与 claude 会话层的 `armIdleTimer` 同口径;按 0
|
|
139
139
|
* setTimeout 会秒删刚建好的连接,等于每条消息重起一次进程)。
|
|
140
140
|
*/
|
|
141
|
-
notifyIdle(
|
|
141
|
+
notifyIdle(cwd: string): void;
|
|
142
142
|
private createClient;
|
|
143
143
|
private clearIdleTimer;
|
|
144
144
|
private disposeSlot;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ConnectionManager: manages agent-server connections per
|
|
2
|
+
* ConnectionManager: manages agent-server connections per cwd, shared by
|
|
3
3
|
* all workspace-lifetime JSON-line runners (codex app-server, kimi acp,
|
|
4
4
|
* opencode acp, and pi rpc).
|
|
5
5
|
*
|
|
6
|
-
* Each
|
|
6
|
+
* Each cwd gets its own connection (transport + client). Connections are
|
|
7
7
|
* created on demand via `acquire()`, cached for reuse, and released after an
|
|
8
8
|
* idle timeout (30 minutes default).
|
|
9
9
|
*
|
|
@@ -62,13 +62,13 @@ export class ConnectionManager {
|
|
|
62
62
|
this.stoppers = opts.stoppers ?? agentStopperRegistry;
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
|
-
* Acquire a connection for the given
|
|
65
|
+
* Acquire a connection for the given cwd.
|
|
66
66
|
* Creates a new connection if one does not exist or the previous one was lost
|
|
67
67
|
* (or, for session-bound protocols, bound to a different session).
|
|
68
68
|
* Serializes creation so concurrent calls share the same connection.
|
|
69
69
|
*/
|
|
70
|
-
async acquire(
|
|
71
|
-
const existing = this.slots.get(
|
|
70
|
+
async acquire(cwd, req = {}) {
|
|
71
|
+
const existing = this.slots.get(cwd);
|
|
72
72
|
if (existing) {
|
|
73
73
|
// If a create is in flight, wait for it
|
|
74
74
|
if (existing.createPromise) {
|
|
@@ -85,10 +85,10 @@ export class ConnectionManager {
|
|
|
85
85
|
})) {
|
|
86
86
|
return existing.client;
|
|
87
87
|
}
|
|
88
|
-
this.disposeSlot(
|
|
88
|
+
this.disposeSlot(cwd);
|
|
89
89
|
}
|
|
90
|
-
const createPromise = this.createClient(
|
|
91
|
-
this.slots.set(
|
|
90
|
+
const createPromise = this.createClient(cwd, req);
|
|
91
|
+
this.slots.set(cwd, {
|
|
92
92
|
client: null, // placeholder
|
|
93
93
|
boundSessionId: req.sessionId,
|
|
94
94
|
idleTimer: null,
|
|
@@ -99,11 +99,11 @@ export class ConnectionManager {
|
|
|
99
99
|
// 竞态:创建期间 release()/disposeAll() 已把 slot 删掉(并可能已
|
|
100
100
|
// dispose 连接)。此时不能再把新连接塞回 slot——否则会复活一个无人管理、
|
|
101
101
|
// 无 idle timer 的连接泄漏。归还给调用者继续使用,但不再缓存。
|
|
102
|
-
const slotAfterCreate = this.slots.get(
|
|
102
|
+
const slotAfterCreate = this.slots.get(cwd);
|
|
103
103
|
if (!slotAfterCreate || slotAfterCreate.createPromise !== createPromise) {
|
|
104
104
|
return client;
|
|
105
105
|
}
|
|
106
|
-
this.slots.set(
|
|
106
|
+
this.slots.set(cwd, {
|
|
107
107
|
client,
|
|
108
108
|
boundSessionId: req.sessionId,
|
|
109
109
|
idleTimer: null,
|
|
@@ -114,9 +114,9 @@ export class ConnectionManager {
|
|
|
114
114
|
catch (err) {
|
|
115
115
|
// 只删除自己的槽位:并发 release + 重新 acquire 后 slot 可能已被新的
|
|
116
116
|
// createPromise 占据,误删会破坏新连接。
|
|
117
|
-
const current = this.slots.get(
|
|
117
|
+
const current = this.slots.get(cwd);
|
|
118
118
|
if (current && current.createPromise === createPromise) {
|
|
119
|
-
this.slots.delete(
|
|
119
|
+
this.slots.delete(cwd);
|
|
120
120
|
}
|
|
121
121
|
throw err;
|
|
122
122
|
}
|
|
@@ -126,16 +126,16 @@ export class ConnectionManager {
|
|
|
126
126
|
* via a protocol `get_state` after a fresh spawn). This lets a subsequent run
|
|
127
127
|
* resume the SAME session on the live connection instead of respawning it.
|
|
128
128
|
*/
|
|
129
|
-
bindSession(
|
|
130
|
-
const slot = this.slots.get(
|
|
129
|
+
bindSession(cwd, sessionId) {
|
|
130
|
+
const slot = this.slots.get(cwd);
|
|
131
131
|
if (slot)
|
|
132
132
|
slot.boundSessionId = sessionId;
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
135
|
-
* Release (dispose) a connection for the given
|
|
135
|
+
* Release (dispose) a connection for the given cwd.
|
|
136
136
|
*/
|
|
137
|
-
async release(
|
|
138
|
-
this.disposeSlot(
|
|
137
|
+
async release(cwd) {
|
|
138
|
+
this.disposeSlot(cwd);
|
|
139
139
|
}
|
|
140
140
|
/**
|
|
141
141
|
* Dispose all connections.
|
|
@@ -145,46 +145,46 @@ export class ConnectionManager {
|
|
|
145
145
|
await Promise.all(workspaces.map((w) => this.disposeSlot(w)));
|
|
146
146
|
}
|
|
147
147
|
/**
|
|
148
|
-
* Notify that the
|
|
148
|
+
* Notify that the cwd is active — disarm the idle timer.
|
|
149
149
|
*/
|
|
150
|
-
notifyActivity(
|
|
151
|
-
const slot = this.slots.get(
|
|
150
|
+
notifyActivity(cwd) {
|
|
151
|
+
const slot = this.slots.get(cwd);
|
|
152
152
|
if (slot) {
|
|
153
153
|
this.clearIdleTimer(slot);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
157
|
-
* Notify that the
|
|
157
|
+
* Notify that the cwd is idle — arm the idle timer.
|
|
158
158
|
*
|
|
159
159
|
* `idleTtlMs: 0` = 不回收(与 claude 会话层的 `armIdleTimer` 同口径;按 0
|
|
160
160
|
* setTimeout 会秒删刚建好的连接,等于每条消息重起一次进程)。
|
|
161
161
|
*/
|
|
162
|
-
notifyIdle(
|
|
162
|
+
notifyIdle(cwd) {
|
|
163
163
|
if (this.idleTtlMs <= 0)
|
|
164
164
|
return;
|
|
165
|
-
const slot = this.slots.get(
|
|
165
|
+
const slot = this.slots.get(cwd);
|
|
166
166
|
if (slot) {
|
|
167
167
|
this.clearIdleTimer(slot);
|
|
168
168
|
slot.idleTimer = setTimeout(() => {
|
|
169
|
-
getLogger().info(`[${this.logTag}] idle timeout for
|
|
170
|
-
this.disposeSlot(
|
|
169
|
+
getLogger().info(`[${this.logTag}] idle timeout for cwd=${cwd}, releasing connection`);
|
|
170
|
+
this.disposeSlot(cwd);
|
|
171
171
|
}, this.idleTtlMs);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
// =========================================================================
|
|
175
175
|
// Internal
|
|
176
176
|
// =========================================================================
|
|
177
|
-
async createClient(
|
|
177
|
+
async createClient(cwd, req) {
|
|
178
178
|
let client = null;
|
|
179
179
|
const transport = new JsonlRpcTransport({
|
|
180
180
|
binary: this.binary,
|
|
181
181
|
args: this.buildArgs(req),
|
|
182
|
-
cwd:
|
|
182
|
+
cwd: cwd,
|
|
183
183
|
env: this.env,
|
|
184
184
|
stoppers: this.stoppers,
|
|
185
185
|
// 通道工厂要拿到**连接本身**:协议取消(codex turn/interrupt、ACP
|
|
186
186
|
// session/cancel)必须发在这条连接上,不能借 runner 的共享状态——那会
|
|
187
|
-
// 打断另一
|
|
187
|
+
// 打断另一 cwd 正在跑的 turn。工厂是在 transport.start() 内侧调用
|
|
188
188
|
// 的,那时 client 已由下面的 clientFactory 赋好。
|
|
189
189
|
stopper: this.stopper
|
|
190
190
|
? (pid) => this.stopper?.({ pid, client: client })
|
|
@@ -194,12 +194,12 @@ export class ConnectionManager {
|
|
|
194
194
|
transport,
|
|
195
195
|
requestTimeoutMs: this.requestTimeoutMs,
|
|
196
196
|
baseOnClose: () => {
|
|
197
|
-
getLogger().info(`[${this.logTag}] connection closed
|
|
197
|
+
getLogger().info(`[${this.logTag}] connection closed cwd=${cwd}`);
|
|
198
198
|
// 只清理自己创建的 slot:并发 release + 重新 acquire 后 slot 可能已被
|
|
199
199
|
// 新连接占据,误删会破坏新连接。
|
|
200
|
-
if (this.slots.get(
|
|
201
|
-
this.slots.delete(
|
|
202
|
-
this.onConnectionLost?.(
|
|
200
|
+
if (this.slots.get(cwd)?.client === client) {
|
|
201
|
+
this.slots.delete(cwd);
|
|
202
|
+
this.onConnectionLost?.(cwd);
|
|
203
203
|
}
|
|
204
204
|
},
|
|
205
205
|
});
|
|
@@ -212,18 +212,18 @@ export class ConnectionManager {
|
|
|
212
212
|
slot.idleTimer = null;
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
-
async disposeSlot(
|
|
216
|
-
const slot = this.slots.get(
|
|
215
|
+
async disposeSlot(cwd) {
|
|
216
|
+
const slot = this.slots.get(cwd);
|
|
217
217
|
if (!slot)
|
|
218
218
|
return;
|
|
219
|
-
this.slots.delete(
|
|
219
|
+
this.slots.delete(cwd);
|
|
220
220
|
this.clearIdleTimer(slot);
|
|
221
221
|
if (slot.client) {
|
|
222
222
|
try {
|
|
223
223
|
await slot.client.dispose();
|
|
224
224
|
}
|
|
225
225
|
catch (err) {
|
|
226
|
-
getLogger().warn(`[${this.logTag}] dispose error
|
|
226
|
+
getLogger().warn(`[${this.logTag}] dispose error cwd=${cwd}: ${err}`);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Normalized usage extracted from a result event.
|
|
3
3
|
*
|
|
4
|
-
* @module runner/common/usage
|
|
4
|
+
* @module runner/common/result-usage
|
|
5
5
|
*/
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
// Implementation
|
|
@@ -30,4 +30,4 @@ export function normalizeResultUsage(usage) {
|
|
|
30
30
|
contextLength,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
//# sourceMappingURL=usage.js.map
|
|
33
|
+
//# sourceMappingURL=result-usage.js.map
|
|
@@ -86,7 +86,7 @@ export declare abstract class SpawningRunner {
|
|
|
86
86
|
private stopperRegistration;
|
|
87
87
|
constructor(opts: {
|
|
88
88
|
pidDir?: string;
|
|
89
|
-
|
|
89
|
+
cwd: string;
|
|
90
90
|
stopGraceMs?: number;
|
|
91
91
|
spawnHeartbeatMs?: number;
|
|
92
92
|
/**
|
|
@@ -127,7 +127,7 @@ export declare abstract class SpawningRunner {
|
|
|
127
127
|
* finite timeout (review P2-11) against the 'error' event: Node guarantees
|
|
128
128
|
* 'error' for ENOENT/EACCES, but some binaries fail silently without ever
|
|
129
129
|
* emitting it. Without the race, the spawn lead-in hangs forever → the
|
|
130
|
-
*
|
|
130
|
+
* cwd serial queue never settles → permanent deadlock, and /stop
|
|
131
131
|
* can't recover (Terminator.stop returns early when pid === undefined).
|
|
132
132
|
* The timeout keeps the deadlock bounded.
|
|
133
133
|
*/
|
|
@@ -2,7 +2,7 @@ import { spawnProcess, isWindowsCommandNotFoundLine, useDetachedProcessGroup, }
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import fs from 'node:fs';
|
|
5
|
-
import {
|
|
5
|
+
import { bestEffortUnlink } from '../../common/fs.js';
|
|
6
6
|
import { getLogger } from '../../logger/index.js';
|
|
7
7
|
import { binaryName, verifyPidIdentityVerdictSync } from '../../platform/identity.js';
|
|
8
8
|
import { createTerminator } from '../../platform/terminator.js';
|
|
@@ -120,7 +120,7 @@ export class SpawningRunner {
|
|
|
120
120
|
this.logTag = opts.logTag ?? 'spawning-runner';
|
|
121
121
|
this.agent = opts.agent;
|
|
122
122
|
const pidDir = opts.pidDir ?? path.join(os.homedir(), '.lark-remote');
|
|
123
|
-
const workspaceSuffix = `-${opts.
|
|
123
|
+
const workspaceSuffix = `-${opts.cwd.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
|
124
124
|
const pidFilePrefix = opts.pidFilePrefix ?? 'spawning';
|
|
125
125
|
this.pidFilePath = path.join(pidDir, `${pidFilePrefix}${workspaceSuffix}.pid`);
|
|
126
126
|
this.terminator =
|
|
@@ -158,7 +158,7 @@ export class SpawningRunner {
|
|
|
158
158
|
* finite timeout (review P2-11) against the 'error' event: Node guarantees
|
|
159
159
|
* 'error' for ENOENT/EACCES, but some binaries fail silently without ever
|
|
160
160
|
* emitting it. Without the race, the spawn lead-in hangs forever → the
|
|
161
|
-
*
|
|
161
|
+
* cwd serial queue never settles → permanent deadlock, and /stop
|
|
162
162
|
* can't recover (Terminator.stop returns early when pid === undefined).
|
|
163
163
|
* The timeout keeps the deadlock bounded.
|
|
164
164
|
*/
|
|
@@ -298,7 +298,7 @@ export class SpawningRunner {
|
|
|
298
298
|
// (immediate 路径不查表,但一并注销同样正确——进程已进入终止流程)。
|
|
299
299
|
this.unregisterStopper();
|
|
300
300
|
this.currentProcess = null;
|
|
301
|
-
|
|
301
|
+
bestEffortUnlink(this.pidFilePath);
|
|
302
302
|
getLogger().debug(`[${this.logTag}] cleaned pid file ${this.pidFilePath}`);
|
|
303
303
|
}
|
|
304
304
|
/**
|
|
@@ -324,7 +324,7 @@ export class SpawningRunner {
|
|
|
324
324
|
const pidStr = fs.readFileSync(this.pidFilePath, 'utf-8').trim();
|
|
325
325
|
const pid = Number(pidStr);
|
|
326
326
|
if (isNaN(pid) || pid <= 0) {
|
|
327
|
-
|
|
327
|
+
bestEffortUnlink(this.pidFilePath);
|
|
328
328
|
return;
|
|
329
329
|
}
|
|
330
330
|
const expectedBinary = binaryName(this.binary);
|
|
@@ -339,7 +339,7 @@ export class SpawningRunner {
|
|
|
339
339
|
if (verdict !== 'match') {
|
|
340
340
|
getLogger().warn(`[${this.logTag}] killOrphan: pid ${pid} not confirmed as ${expectedBinary} ` +
|
|
341
341
|
`(${verdict}), skipping kill`);
|
|
342
|
-
|
|
342
|
+
bestEffortUnlink(this.pidFilePath);
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
345
345
|
// 身份匹配:杀整个进程组(detached:true 下 agent 是组长),与
|
|
@@ -351,7 +351,7 @@ export class SpawningRunner {
|
|
|
351
351
|
catch {
|
|
352
352
|
// process may have exited
|
|
353
353
|
}
|
|
354
|
-
|
|
354
|
+
bestEffortUnlink(this.pidFilePath);
|
|
355
355
|
}
|
|
356
356
|
catch {
|
|
357
357
|
// ignore
|
|
@@ -401,7 +401,7 @@ export class SpawningRunner {
|
|
|
401
401
|
catch { }
|
|
402
402
|
}
|
|
403
403
|
this.unregisterStopper();
|
|
404
|
-
|
|
404
|
+
bestEffortUnlink(this.pidFilePath);
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
//# sourceMappingURL=spawning-runner.js.map
|
|
@@ -12,10 +12,10 @@ import type { AgentEvent } from '../types.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function syntheticInitEvent(sessionId?: string): AgentEvent;
|
|
14
14
|
/**
|
|
15
|
-
* Build a standardized
|
|
15
|
+
* Build a standardized setup-failure result event for yield.
|
|
16
16
|
*
|
|
17
|
-
* Used when
|
|
18
|
-
* session creation
|
|
17
|
+
* Used when setup fails before a turn can run — process spawn errors
|
|
18
|
+
* (e.g. ENOENT), not-logged-in, or session creation failure. Produces a
|
|
19
19
|
* `result`/`error` event so the bridge can render a card instead of crashing.
|
|
20
20
|
*/
|
|
21
|
-
export declare function
|
|
21
|
+
export declare function setupErrorEvent(errorMessage: string, sessionId?: string): AgentEvent;
|
|
@@ -20,13 +20,13 @@ export function syntheticInitEvent(sessionId = '') {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Build a standardized
|
|
23
|
+
* Build a standardized setup-failure result event for yield.
|
|
24
24
|
*
|
|
25
|
-
* Used when
|
|
26
|
-
* session creation
|
|
25
|
+
* Used when setup fails before a turn can run — process spawn errors
|
|
26
|
+
* (e.g. ENOENT), not-logged-in, or session creation failure. Produces a
|
|
27
27
|
* `result`/`error` event so the bridge can render a card instead of crashing.
|
|
28
28
|
*/
|
|
29
|
-
export function
|
|
29
|
+
export function setupErrorEvent(errorMessage, sessionId = '') {
|
|
30
30
|
return {
|
|
31
31
|
type: 'result',
|
|
32
32
|
subtype: 'error',
|
|
@@ -35,4 +35,4 @@ export function authErrorEvent(errorMessage, sessionId = '') {
|
|
|
35
35
|
timestamp: new Date().toISOString(),
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
//# sourceMappingURL=
|
|
38
|
+
//# sourceMappingURL=synthetic-events.js.map
|
|
@@ -86,7 +86,7 @@ export declare class DshClient {
|
|
|
86
86
|
* `session.history` to fill the gap (dedup by seq). Terminates on turn/end
|
|
87
87
|
* or `signal` abort.
|
|
88
88
|
*/
|
|
89
|
-
sessionEvents(sessionId: string,
|
|
89
|
+
sessionEvents(sessionId: string, signal: AbortSignal, initialSeq?: number): AsyncGenerator<DshStreamItem>;
|
|
90
90
|
/** Extract TokenUsage from an assistant/message data payload (absent → undefined). */
|
|
91
91
|
static usageOf(data: Record<string, unknown>): DshTokenUsage | undefined;
|
|
92
92
|
}
|
|
@@ -320,7 +320,7 @@ export class DshClient {
|
|
|
320
320
|
* `session.history` to fill the gap (dedup by seq). Terminates on turn/end
|
|
321
321
|
* or `signal` abort.
|
|
322
322
|
*/
|
|
323
|
-
async *sessionEvents(sessionId,
|
|
323
|
+
async *sessionEvents(sessionId, signal, initialSeq = -1) {
|
|
324
324
|
const state = { lastSeq: initialSeq };
|
|
325
325
|
// Initial baseline: before opening the first live mux subscription, replay
|
|
326
326
|
// history once so events produced between prompt-accept and the mux opening
|
|
@@ -135,7 +135,7 @@ export class DshRunner {
|
|
|
135
135
|
});
|
|
136
136
|
const translator = new DshTranslator();
|
|
137
137
|
try {
|
|
138
|
-
for await (const item of this.client.sessionEvents(sessionId,
|
|
138
|
+
for await (const item of this.client.sessionEvents(sessionId, abort.signal, prePromptSeq)) {
|
|
139
139
|
if (abort.signal.aborted)
|
|
140
140
|
break;
|
|
141
141
|
if (item.kind === 'approval') {
|
|
@@ -21,7 +21,7 @@ import type { AgentKind, AgentSessionReader, AgentStatusInfo, SpawnOptions } fro
|
|
|
21
21
|
import { JsonRpcClient } from '../../common/jsonrpc/client.js';
|
|
22
22
|
import { KimiAcpTranslator } from './translator.js';
|
|
23
23
|
import { type SessionPromptResult } from '../../common/acp/protocol-types.js';
|
|
24
|
-
import { type AcpPendingApproval } from '../../common/acp/
|
|
24
|
+
import { type AcpPendingApproval } from '../../common/acp/acp-approval.js';
|
|
25
25
|
import { BaseAcpRunner } from '../../common/acp/base-acp-runner.js';
|
|
26
26
|
export interface KimiAcpRunnerOptions {
|
|
27
27
|
kind: AgentKind;
|
|
@@ -23,7 +23,7 @@ import { StringDecoder } from 'node:string_decoder';
|
|
|
23
23
|
import { ConnectionManager, } from '../../common/jsonrpc/connection-manager.js';
|
|
24
24
|
import { KimiAcpTranslator } from './translator.js';
|
|
25
25
|
import { NotificationMethod, RpcErrorCode, ServerRequestMethod, } from '../../common/acp/protocol-types.js';
|
|
26
|
-
import { KIMI_APPROVAL_KINDS, buildAcpPermissionOutcome, } from '../../common/acp/
|
|
26
|
+
import { KIMI_APPROVAL_KINDS, buildAcpPermissionOutcome, } from '../../common/acp/acp-approval.js';
|
|
27
27
|
import { mapAnswersByIndex } from '../../question-common.js';
|
|
28
28
|
import { getLogger } from '../../../logger/index.js';
|
|
29
29
|
import { BaseAcpRunner } from '../../common/acp/base-acp-runner.js';
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { makeQuestionApprovalEvent } from '../../question-common.js';
|
|
29
29
|
import { ServerRequestMethod, } from '../../common/acp/protocol-types.js';
|
|
30
|
-
import { deriveAcpAvailableDecisions, truncateWithEllipsis, } from '../../common/acp/
|
|
30
|
+
import { deriveAcpAvailableDecisions, truncateWithEllipsis, } from '../../common/acp/acp-approval.js';
|
|
31
31
|
import { getLogger } from '../../../logger/index.js';
|
|
32
32
|
import { BaseAcpTranslator } from '../../common/acp/base-translator.js';
|
|
33
33
|
/**
|
|
@@ -23,7 +23,7 @@ import type { AgentKind, AgentSessionReader, AgentStatusInfo } from '../../types
|
|
|
23
23
|
import { JsonRpcClient } from '../../common/jsonrpc/client.js';
|
|
24
24
|
import { OpencodeAcpTranslator } from './translator.js';
|
|
25
25
|
import { type SessionNewResult, type SessionResumeResult, type SessionPromptResult } from '../../common/acp/protocol-types.js';
|
|
26
|
-
import { type AcpPendingApproval } from '../../common/acp/
|
|
26
|
+
import { type AcpPendingApproval } from '../../common/acp/acp-approval.js';
|
|
27
27
|
import { BaseAcpRunner } from '../../common/acp/base-acp-runner.js';
|
|
28
28
|
export interface OpencodeAcpRunnerOptions {
|
|
29
29
|
kind: AgentKind;
|
|
@@ -34,7 +34,7 @@ export interface OpencodeAcpRunnerOptions {
|
|
|
34
34
|
env?: Record<string, string | undefined>;
|
|
35
35
|
/** Args to spawn the ACP server with. Defaults to `['acp']`. Note: no
|
|
36
36
|
* explicit `--cwd` — opencode acp defaults --cwd to the process cwd, which
|
|
37
|
-
* the transport already sets to the
|
|
37
|
+
* the transport already sets to the cwd (cli/cmd/acp.ts:13-17). */
|
|
38
38
|
acpArgs?: string[];
|
|
39
39
|
/** Request timeout in milliseconds. */
|
|
40
40
|
requestTimeoutMs?: number;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { ConnectionManager, } from '../../common/jsonrpc/connection-manager.js';
|
|
23
23
|
import { OpencodeAcpTranslator } from './translator.js';
|
|
24
24
|
import { RpcErrorCode, } from '../../common/acp/protocol-types.js';
|
|
25
|
-
import { OPENCODE_APPROVAL_KINDS, buildAcpPermissionOutcome, } from '../../common/acp/
|
|
25
|
+
import { OPENCODE_APPROVAL_KINDS, buildAcpPermissionOutcome, } from '../../common/acp/acp-approval.js';
|
|
26
26
|
import { getLogger } from '../../../logger/index.js';
|
|
27
27
|
import { BaseAcpRunner } from '../../common/acp/base-acp-runner.js';
|
|
28
28
|
import { OpencodeLogErrorMonitor, resolveOpencodeLogPath } from './error-monitor.js';
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* prompt other stopReason/error → result error
|
|
25
25
|
* session/request_permission → approval_requested (optionId echo)
|
|
26
26
|
*/
|
|
27
|
-
import { deriveAcpAvailableDecisions, truncateWithEllipsis, } from '../../common/acp/
|
|
27
|
+
import { deriveAcpAvailableDecisions, truncateWithEllipsis, } from '../../common/acp/acp-approval.js';
|
|
28
28
|
import { getLogger } from '../../../logger/index.js';
|
|
29
29
|
import { BaseAcpTranslator } from '../../common/acp/base-translator.js';
|
|
30
30
|
// =============================================================================
|
|
@@ -15,7 +15,6 @@ export class PiRpcTranslator {
|
|
|
15
15
|
currentContentIndex = -1;
|
|
16
16
|
sessionId = '';
|
|
17
17
|
operationKind = 'turn';
|
|
18
|
-
currentTurnId = '';
|
|
19
18
|
// ccusage-aligned usage accumulation (per-message, summed across the turn).
|
|
20
19
|
accInput = 0;
|
|
21
20
|
accOutput = 0;
|
|
@@ -134,7 +133,6 @@ export class PiRpcTranslator {
|
|
|
134
133
|
/** Self-produced turn_started (no wire equivalent). */
|
|
135
134
|
produceTurnStarted(sessionId, turnId) {
|
|
136
135
|
this.sessionId = sessionId;
|
|
137
|
-
this.currentTurnId = turnId;
|
|
138
136
|
return {
|
|
139
137
|
type: 'turn_started',
|
|
140
138
|
threadId: sessionId,
|