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.
- package/CHANGELOG.md +19 -0
- package/dist/bridge/approval-actions.d.ts +1 -1
- package/dist/bridge/approval-actions.js +1 -1
- package/dist/bridge/bash-run.d.ts +71 -0
- package/dist/bridge/bash-run.js +217 -0
- package/dist/bridge/compact.d.ts +155 -0
- package/dist/bridge/compact.js +450 -0
- package/dist/bridge/final-usage.js +4 -4
- package/dist/bridge/inbound-media.js +1 -1
- package/dist/bridge/index.d.ts +97 -153
- package/dist/bridge/index.js +182 -809
- package/dist/bridge/job-watch.d.ts +68 -0
- package/dist/bridge/job-watch.js +146 -0
- package/dist/bridge/queue-manager.d.ts +3 -3
- package/dist/bridge/queue-manager.js +10 -29
- package/dist/card/approval-render.d.ts +2 -2
- package/dist/card/approval-render.js +109 -97
- package/dist/card/card-budget.js +1 -1
- package/dist/card/card-helpers.d.ts +3 -3
- package/dist/card/card-helpers.js +6 -6
- package/dist/card/final-answer-file.js +1 -1
- package/dist/card/run-renderer.d.ts +3 -3
- package/dist/card/run-renderer.js +34 -93
- package/dist/card/run-state.d.ts +1 -1
- package/dist/card/run-state.js +28 -90
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +1 -1
- package/dist/card/usage-format.d.ts +12 -0
- package/dist/card/usage-format.js +8 -3
- package/dist/cli.js +15 -3
- package/dist/clone.js +2 -2
- package/dist/config/codex-config.js +130 -106
- package/dist/config/index.d.ts +16 -2
- package/dist/config/index.js +23 -8
- package/dist/connector/index.js +6 -6
- package/dist/error-classification.js +3 -2
- package/dist/inbound/placeholder.d.ts +1 -1
- package/dist/inbound/placeholder.js +1 -1
- package/dist/index.js +203 -133
- package/dist/logger/index.js +6 -6
- package/dist/message-handlers.d.ts +0 -5
- package/dist/message-handlers.js +1 -1
- package/dist/model-catalog.d.ts +2 -2
- package/dist/model-catalog.js +3 -8
- package/dist/platform/identity.js +1 -1
- package/dist/platform/path.d.ts +1 -1
- package/dist/platform/path.js +1 -1
- package/dist/platform/sleep-blocker.js +1 -1
- package/dist/platform/terminator-win32.js +2 -1
- package/dist/router/commands/resume.js +2 -20
- package/dist/router/config/dsh.d.ts +4 -8
- package/dist/router/config/dsh.js +3 -10
- package/dist/router/config/kimi.js +1 -1
- package/dist/router/config/types.d.ts +1 -1
- package/dist/router/config-diff.js +1 -1
- package/dist/router/index.d.ts +56 -11
- package/dist/router/index.js +219 -200
- package/dist/runner/claude/session.d.ts +1 -1
- package/dist/runner/claude/session.js +6 -4
- package/dist/runner/codex/app-server/runner.d.ts +9 -2
- package/dist/runner/codex/app-server/runner.js +30 -25
- package/dist/runner/codex/app-server/translator.d.ts +0 -5
- package/dist/runner/codex/app-server/translator.js +4 -17
- package/dist/runner/common/acp/acp-approval.d.ts +1 -1
- package/dist/runner/common/acp/acp-approval.js +1 -1
- package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
- package/dist/runner/common/acp/base-acp-runner.js +26 -65
- package/dist/runner/common/acp/base-translator.d.ts +12 -2
- package/dist/runner/common/acp/base-translator.js +31 -9
- package/dist/runner/common/acp/protocol-types.d.ts +1 -1
- package/dist/runner/common/acp/protocol-types.js +2 -2
- package/dist/runner/common/connection-based-runner.d.ts +32 -1
- package/dist/runner/common/connection-based-runner.js +32 -2
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
- package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
- package/dist/runner/common/spawning-runner.d.ts +1 -1
- package/dist/runner/common/spawning-runner.js +1 -1
- package/dist/runner/dsh/catalog-probe.d.ts +17 -0
- package/dist/runner/dsh/catalog-probe.js +10 -0
- package/dist/runner/dsh/client.d.ts +1 -3
- package/dist/runner/dsh/client.js +2 -4
- package/dist/runner/dsh/exit-cancel.d.ts +2 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +1 -1
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/runner.d.ts +40 -1
- package/dist/runner/dsh/runner.js +272 -199
- package/dist/runner/dsh/translator.d.ts +1 -1
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +1 -2
- package/dist/runner/kimi/acp/runner.js +1 -2
- package/dist/runner/kimi/acp/translator.d.ts +1 -1
- package/dist/runner/kimi/acp/translator.js +3 -4
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
- package/dist/runner/pi/rpc/protocol-types.js +1 -1
- package/dist/runner/pi/rpc/runner.d.ts +3 -0
- package/dist/runner/pi/rpc/runner.js +27 -42
- package/dist/runner/types.d.ts +2 -2
- package/dist/runner/types.js +1 -1
- package/dist/session/claude/sessions.d.ts +45 -0
- package/dist/session/claude/sessions.js +97 -71
- package/dist/session/codex/rollout-reader.js +4 -4
- package/dist/session/common/throw-session-list-error.d.ts +10 -0
- package/dist/session/common/throw-session-list-error.js +16 -0
- package/dist/session/dsh/sessions.js +11 -5
- package/dist/session/kimi/sessions.d.ts +40 -1
- package/dist/session/kimi/sessions.js +244 -192
- package/dist/session/opencode/sessions.d.ts +153 -1
- package/dist/session/opencode/sessions.js +354 -321
- package/dist/session/pi/sessions.js +4 -5
- package/dist/session/session-store.d.ts +1 -1
- package/dist/session/session-store.js +1 -1
- package/dist/update/version-check.js +2 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.js +1 -1
- package/package.json +1 -1
|
@@ -140,7 +140,7 @@ export declare class ClaudeSession extends SpawningRunner {
|
|
|
140
140
|
* 消费事件队列直到本 turn 的 result 事件(非 compact/compaction)。
|
|
141
141
|
* 消费者提前关闭生成器(bridge 循环体抛错)→ 杀进程防孤儿(语义)。
|
|
142
142
|
*
|
|
143
|
-
* result = turn
|
|
143
|
+
* result = turn 终态(长驻模式下 result 之后
|
|
144
144
|
* 不再有 stdout 事件,后台任务完成是静默的;旧 -p 流程「等进程退出再收尾」
|
|
145
145
|
* 的 post-result drain 在长驻模式下无对应物)。
|
|
146
146
|
*/
|
|
@@ -127,7 +127,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
127
127
|
// 或 --resume 目标会话)。比较进程当前 session(init 报告值)而非 spawn
|
|
128
128
|
// 请求值:bridge 会把 init 的 session_id 写回 SessionStore,第二条消息
|
|
129
129
|
// 带着它来;若拿 spawn 请求值('')比较,每条消息都会误判切换、杀进程
|
|
130
|
-
//
|
|
130
|
+
// 重启,长驻设计失效。
|
|
131
131
|
if (this.isRunning && this.sessionId !== (opts.sessionId ?? '')) {
|
|
132
132
|
getLogger().info(`[${this.logTag}] session mismatch: process=${this.sessionId || '(fresh)'} ` +
|
|
133
133
|
`requested=${opts.sessionId ?? '(fresh)'}, recycling process`);
|
|
@@ -151,7 +151,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
151
151
|
// review:stop()/进程死亡与写 stdin 竞态(EPIPE/ENOTCONN)。用户
|
|
152
152
|
// stop 已置 stoppedByUser(或进程已死/流已结束)时,写入失败不抛给
|
|
153
153
|
// 调用方,交由 consumeTurn 按 interrupted/error 产出终态——否则
|
|
154
|
-
// stop 在途的 run 会以裸 EPIPE 崩溃(
|
|
154
|
+
// stop 在途的 run 会以裸 EPIPE 崩溃(flaky)。
|
|
155
155
|
if (this.stoppedByUser || !this.isRunning || this.streamEnded) {
|
|
156
156
|
getLogger().warn(`[${this.logTag}] user message write failed, process stopping: ${err instanceof Error ? err.message : String(err)}`);
|
|
157
157
|
}
|
|
@@ -383,7 +383,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
383
383
|
* 消费事件队列直到本 turn 的 result 事件(非 compact/compaction)。
|
|
384
384
|
* 消费者提前关闭生成器(bridge 循环体抛错)→ 杀进程防孤儿(语义)。
|
|
385
385
|
*
|
|
386
|
-
* result = turn
|
|
386
|
+
* result = turn 终态(长驻模式下 result 之后
|
|
387
387
|
* 不再有 stdout 事件,后台任务完成是静默的;旧 -p 流程「等进程退出再收尾」
|
|
388
388
|
* 的 post-result drain 在长驻模式下无对应物)。
|
|
389
389
|
*/
|
|
@@ -716,7 +716,9 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
716
716
|
void this.writeControlResponse(requestId, {
|
|
717
717
|
behavior: 'deny',
|
|
718
718
|
message: '无法解析 AskUserQuestion 选项',
|
|
719
|
-
}).catch(() => {
|
|
719
|
+
}).catch((err) => {
|
|
720
|
+
getLogger().warn(`[${this.logTag}] AskUserQuestion deny write failed requestId=${requestId}: ${err.message}`);
|
|
721
|
+
});
|
|
720
722
|
// 解析失败 deny 后同样释放条目(不经过 respondPermission)。
|
|
721
723
|
this.pendingToolInputs.delete(requestId);
|
|
722
724
|
return [];
|
|
@@ -62,7 +62,14 @@ export declare class CodexAppServerRunner extends ConnectionBasedRunner<JsonRpcC
|
|
|
62
62
|
protected cancelCurrentTurn(): Promise<void>;
|
|
63
63
|
protected clearTurnState(): void;
|
|
64
64
|
protected releaseConnection(cwd: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Wire the acquired client's hooks into the turn dispatcher (anchor-guarded).
|
|
67
|
+
* 代际锚点与 ACP/pi 同口径:连接按 workspace 缓存复用,旧 turn 迟到的通知 /
|
|
68
|
+
* server request / onClose 不得进入新轮(此前 codex 缺这层守卫,属补齐漂移)。
|
|
69
|
+
*/
|
|
70
|
+
private installTurnHooks;
|
|
65
71
|
protected notifyIdle(cwd: string): void;
|
|
72
|
+
protected notifyConnectionActivity(cwd: string): void;
|
|
66
73
|
protected disposeConnections(): Promise<void>;
|
|
67
74
|
/**
|
|
68
75
|
* Run a compact operation on the current thread.
|
|
@@ -100,8 +107,8 @@ export declare class CodexAppServerRunner extends ConnectionBasedRunner<JsonRpcC
|
|
|
100
107
|
*
|
|
101
108
|
* App-server 把已知线程的通知全部复用同一条 stdio 连接:agent 用
|
|
102
109
|
* multi-agent v2 派生子 agent 后,子线程的 `turn/started`、`item/*`、
|
|
103
|
-
* `turn/completed`、`thread/tokenUsage/updated`
|
|
104
|
-
*
|
|
110
|
+
* `turn/completed`、`thread/tokenUsage/updated` 会与本轮通知交错到达。
|
|
111
|
+
* 一个 turn 只属于一个线程,因此外来线程的通知一律
|
|
105
112
|
* 丢弃——否则子线程的 `turn/started` 会覆盖本轮 threadId,会话写回把子
|
|
106
113
|
* 线程 id 当成顶层会话指针存盘(冷连接 `thread/resume` 直接报
|
|
107
114
|
* "cannot resume an unloaded multi-agent v2 sub-agent"),且子线程的
|
|
@@ -58,8 +58,8 @@ function buildRequestUserInputResponse(action, questions, response) {
|
|
|
58
58
|
return;
|
|
59
59
|
const values = Array.isArray(value) ? value : [value];
|
|
60
60
|
const cleaned = values.filter((v) => typeof v === 'string' && v.length > 0);
|
|
61
|
-
// Codex user_note:补充说明编码为 "user_note: <text>" 条目(
|
|
62
|
-
//
|
|
61
|
+
// Codex user_note:补充说明编码为 "user_note: <text>" 条目(deepseek-v4-flash
|
|
62
|
+
// 明确理解并遵守该格式)。
|
|
63
63
|
const note = notes?.[q.question]?.trim();
|
|
64
64
|
if (note)
|
|
65
65
|
cleaned.push(`user_note: ${note}`);
|
|
@@ -184,9 +184,27 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
184
184
|
async releaseConnection(cwd) {
|
|
185
185
|
await this.connectionManager.release(cwd);
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Wire the acquired client's hooks into the turn dispatcher (anchor-guarded).
|
|
189
|
+
* 代际锚点与 ACP/pi 同口径:连接按 workspace 缓存复用,旧 turn 迟到的通知 /
|
|
190
|
+
* server request / onClose 不得进入新轮(此前 codex 缺这层守卫,属补齐漂移)。
|
|
191
|
+
*/
|
|
192
|
+
installTurnHooks(anchor) {
|
|
193
|
+
anchor.client.setHooks({
|
|
194
|
+
onNotification: this.forCurrentTurn(anchor, 'notification', (method, params) => this.handleNotification(method, params)),
|
|
195
|
+
onServerRequest: this.forCurrentTurn(anchor, 'server request', (id, method, params) => this.handleServerRequest(id, method, params)),
|
|
196
|
+
onClose: this.forCurrentTurn(anchor, 'connection close', () => {
|
|
197
|
+
getLogger().warn(`[${this.logTag}] client connection closed`);
|
|
198
|
+
this.failTurn('Codex app-server connection closed');
|
|
199
|
+
}),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
187
202
|
notifyIdle(cwd) {
|
|
188
203
|
this.connectionManager.notifyIdle(cwd);
|
|
189
204
|
}
|
|
205
|
+
notifyConnectionActivity(cwd) {
|
|
206
|
+
this.connectionManager.notifyActivity(cwd);
|
|
207
|
+
}
|
|
190
208
|
async disposeConnections() {
|
|
191
209
|
await this.connectionManager.disposeAll();
|
|
192
210
|
}
|
|
@@ -195,14 +213,9 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
195
213
|
*/
|
|
196
214
|
async *runCompact(_message, opts) {
|
|
197
215
|
yield* this.executeTurn(opts, async () => {
|
|
198
|
-
const
|
|
199
|
-
this.
|
|
200
|
-
|
|
201
|
-
client.setHooks({
|
|
202
|
-
onNotification: (method, params) => this.handleNotification(method, params),
|
|
203
|
-
onServerRequest: (id, method, params) => this.handleServerRequest(id, method, params),
|
|
204
|
-
onClose: () => this.failTurn('Codex app-server connection closed'),
|
|
205
|
-
});
|
|
216
|
+
const anchor = await this.acquireForTurn(opts.cwd, () => this.connectionManager.acquire(opts.cwd));
|
|
217
|
+
this.installTurnHooks(anchor);
|
|
218
|
+
const { client } = anchor;
|
|
206
219
|
if (!opts.sessionId) {
|
|
207
220
|
throw new Error('compact requires a sessionId');
|
|
208
221
|
}
|
|
@@ -244,8 +257,8 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
244
257
|
else if (pending.kind === 'permissions') {
|
|
245
258
|
// 权限审批的响应没有 decision 字段(真实协议只回 granted profile +
|
|
246
259
|
// scope):decline/cancel 必须返回空授权(拒绝全部),否则会把用户已
|
|
247
|
-
//
|
|
248
|
-
//
|
|
260
|
+
// 勾选的条目当作授予返回(勾选后点「拒绝」实际授予了所选权限)。
|
|
261
|
+
// accept/acceptForSession 才带用户勾选的条目。
|
|
249
262
|
const denied = action === 'decline' || action === 'cancel';
|
|
250
263
|
client.respond(requestId, {
|
|
251
264
|
permissions: denied
|
|
@@ -344,17 +357,9 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
344
357
|
* thread/resume(按 sessionId 恢复既有线程)→ turn/start。
|
|
345
358
|
*/
|
|
346
359
|
async setupTurn(message, opts) {
|
|
347
|
-
const
|
|
348
|
-
this.
|
|
349
|
-
|
|
350
|
-
client.setHooks({
|
|
351
|
-
onNotification: (method, params) => this.handleNotification(method, params),
|
|
352
|
-
onServerRequest: (id, method, params) => this.handleServerRequest(id, method, params),
|
|
353
|
-
onClose: () => {
|
|
354
|
-
getLogger().warn(`[${this.logTag}] client connection closed`);
|
|
355
|
-
this.failTurn('Codex app-server connection closed');
|
|
356
|
-
},
|
|
357
|
-
});
|
|
360
|
+
const anchor = await this.acquireForTurn(opts.cwd, () => this.connectionManager.acquire(opts.cwd));
|
|
361
|
+
this.installTurnHooks(anchor);
|
|
362
|
+
const { client } = anchor;
|
|
358
363
|
const threadParams = this.buildThreadParams(opts.cwd);
|
|
359
364
|
let threadId;
|
|
360
365
|
if (opts.sessionId) {
|
|
@@ -387,8 +392,8 @@ export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
|
387
392
|
*
|
|
388
393
|
* App-server 把已知线程的通知全部复用同一条 stdio 连接:agent 用
|
|
389
394
|
* multi-agent v2 派生子 agent 后,子线程的 `turn/started`、`item/*`、
|
|
390
|
-
* `turn/completed`、`thread/tokenUsage/updated`
|
|
391
|
-
*
|
|
395
|
+
* `turn/completed`、`thread/tokenUsage/updated` 会与本轮通知交错到达。
|
|
396
|
+
* 一个 turn 只属于一个线程,因此外来线程的通知一律
|
|
392
397
|
* 丢弃——否则子线程的 `turn/started` 会覆盖本轮 threadId,会话写回把子
|
|
393
398
|
* 线程 id 当成顶层会话指针存盘(冷连接 `thread/resume` 直接报
|
|
394
399
|
* "cannot resume an unloaded multi-agent v2 sub-agent"),且子线程的
|
|
@@ -14,7 +14,6 @@ export type TranslatorEvent = AgentEvent | TurnStartedEvent | TurnDiffEvent | Ap
|
|
|
14
14
|
export declare class CodexAppServerTranslator {
|
|
15
15
|
private turnId;
|
|
16
16
|
private threadId;
|
|
17
|
-
private turnStatus;
|
|
18
17
|
/** Full agentMessage text per item id (item-scoped snapshot). */
|
|
19
18
|
private textByItem;
|
|
20
19
|
/** agentMessage item ids whose protocol phase is `final_answer`. */
|
|
@@ -69,10 +68,6 @@ export declare class CodexAppServerTranslator {
|
|
|
69
68
|
inputTokens: number;
|
|
70
69
|
outputTokens: number;
|
|
71
70
|
};
|
|
72
|
-
/**
|
|
73
|
-
* Get the current turn status.
|
|
74
|
-
*/
|
|
75
|
-
getTurnStatus(): string;
|
|
76
71
|
/**
|
|
77
72
|
* Set the operation kind (exec vs compact).
|
|
78
73
|
*/
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* app-server v2 protocol (see protocol-types.ts).
|
|
9
9
|
*/
|
|
10
10
|
import { makeQuestionApprovalEvent } from '../../question-common.js';
|
|
11
|
+
import { renderPlanMarkdown } from '../../common/acp/base-translator.js';
|
|
11
12
|
import { NotificationMethod, ServerRequestMethod, UNSUPPORTED_SERVER_REQUEST_METHODS, } from './protocol-types.js';
|
|
12
13
|
// =============================================================================
|
|
13
14
|
// Translator
|
|
@@ -15,7 +16,6 @@ import { NotificationMethod, ServerRequestMethod, UNSUPPORTED_SERVER_REQUEST_MET
|
|
|
15
16
|
export class CodexAppServerTranslator {
|
|
16
17
|
turnId = '';
|
|
17
18
|
threadId = '';
|
|
18
|
-
turnStatus = 'pending';
|
|
19
19
|
/** Full agentMessage text per item id (item-scoped snapshot). */
|
|
20
20
|
textByItem = new Map();
|
|
21
21
|
/** agentMessage item ids whose protocol phase is `final_answer`. */
|
|
@@ -133,7 +133,6 @@ export class CodexAppServerTranslator {
|
|
|
133
133
|
*/
|
|
134
134
|
handleTurnCompleted(params) {
|
|
135
135
|
const turn = params?.turn;
|
|
136
|
-
this.turnStatus = turn?.status ?? 'completed';
|
|
137
136
|
const events = [];
|
|
138
137
|
// 兜底:turn.items 里权威 agentMessage / plan item 与已流式内容不一致或
|
|
139
138
|
// 未发过(如 itemsView 摘要只含部分 item)时,补发 item 级快照。已一致则
|
|
@@ -204,12 +203,6 @@ export class CodexAppServerTranslator {
|
|
|
204
203
|
outputTokens: this.usage.outputTokens,
|
|
205
204
|
};
|
|
206
205
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Get the current turn status.
|
|
209
|
-
*/
|
|
210
|
-
getTurnStatus() {
|
|
211
|
-
return this.turnStatus;
|
|
212
|
-
}
|
|
213
206
|
/**
|
|
214
207
|
* Set the operation kind (exec vs compact).
|
|
215
208
|
*/
|
|
@@ -222,7 +215,6 @@ export class CodexAppServerTranslator {
|
|
|
222
215
|
handleTurnStarted(params) {
|
|
223
216
|
this.threadId = params.threadId;
|
|
224
217
|
this.turnId = params.turn.id;
|
|
225
|
-
this.turnStatus = params.turn.status ?? 'inProgress';
|
|
226
218
|
// 单 turn 用量从零开始累计:turn 内每次采样都会发一条
|
|
227
219
|
// `thread/tokenUsage/updated`,本 run 的用量是这些增量之和(不是最后一条)。
|
|
228
220
|
// resume 后服务端重放的上一 turn 用量若在此之后到达,由 handleTokenUsageUpdated
|
|
@@ -497,14 +489,9 @@ export class CodexAppServerTranslator {
|
|
|
497
489
|
* status 缺省或未知一律按 pending 处理(防御 wire 枚举漂移)。
|
|
498
490
|
*/
|
|
499
491
|
handleTurnPlanUpdated(params) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const status = s.status && s.status in ICON ? s.status : 'pending';
|
|
504
|
-
return `${ICON[status]} ${s.title}${s.subtitle ? ` — ${s.subtitle}` : ''}`;
|
|
505
|
-
})
|
|
506
|
-
.join('\n');
|
|
507
|
-
return [{ type: 'plan', plan: markdown, timestamp: this.now() }];
|
|
492
|
+
// 图标表 + status 归一与 ACP handlePlan 共用 renderPlanMarkdown
|
|
493
|
+
// (未知/缺省 status 一律按 pending,防 wire 枚举漂移)。
|
|
494
|
+
return [{ type: 'plan', plan: renderPlanMarkdown(params.plan), timestamp: this.now() }];
|
|
508
495
|
}
|
|
509
496
|
handleError(params) {
|
|
510
497
|
if (params.willRetry) {
|
|
@@ -13,7 +13,7 @@ export interface AcpApprovalKinds {
|
|
|
13
13
|
acceptForSession: string[];
|
|
14
14
|
decline: string[];
|
|
15
15
|
}
|
|
16
|
-
/** kimi 观测 kind(approval.ts:28-29
|
|
16
|
+
/** kimi 观测 kind(approval.ts:28-29:approve_once/approve_always/reject)。 */
|
|
17
17
|
export declare const KIMI_APPROVAL_KINDS: AcpApprovalKinds;
|
|
18
18
|
/** opencode kind(permission.ts:219-223:once/always → approve,其余 → reject)。 */
|
|
19
19
|
export declare const OPENCODE_APPROVAL_KINDS: AcpApprovalKinds;
|
|
@@ -20,7 +20,7 @@ function findOptionIdByKind(options, candidateKinds) {
|
|
|
20
20
|
}
|
|
21
21
|
return undefined;
|
|
22
22
|
}
|
|
23
|
-
/** kimi 观测 kind(approval.ts:28-29
|
|
23
|
+
/** kimi 观测 kind(approval.ts:28-29:approve_once/approve_always/reject)。 */
|
|
24
24
|
export const KIMI_APPROVAL_KINDS = {
|
|
25
25
|
accept: ['approve_once', 'approve_always', 'allow_once'],
|
|
26
26
|
acceptForSession: ['approve_always', 'allow_always'],
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* prompt orchestration, compact orchestration, approval response dispatch,
|
|
7
7
|
* cancel/clear/release plumbing and the pendingApprovals registry were
|
|
8
8
|
* line-for-line duplicated with only constants/field names differing
|
|
9
|
-
* (
|
|
9
|
+
* (round-2 simplification). This base absorbs them; what stays in
|
|
10
10
|
* subclasses:
|
|
11
11
|
* - applyTurnSettings: mode/model下发 semantics differ (kimi unconditional,
|
|
12
12
|
* opencode conditional on cached values)
|
|
@@ -80,7 +80,7 @@ export declare abstract class BaseAcpRunner<TTranslator extends BaseAcpTranslato
|
|
|
80
80
|
* 自身半死:opencode v2 的 location 在每轮拉起的 `opencode acp` 子进程里 boot,
|
|
81
81
|
* boot 期间后台服务不可用会让它长期停在未激活,后续轮次复用同一连接只会重复
|
|
82
82
|
* 失败——用户重启 opencode service 也治不好,因为连接缓存在本进程里
|
|
83
|
-
* (
|
|
83
|
+
* (4 次重试全失败,重启服务与 lark-remote 都无效)。丢掉连接
|
|
84
84
|
* 后下一轮重新 spawn,拿到的是新进程的新状态。
|
|
85
85
|
*
|
|
86
86
|
* 只对声明为可重试的错误生效:kimi 等沿用默认 `isRetryableSessionSetupError`
|
|
@@ -94,6 +94,9 @@ export declare abstract class BaseAcpRunner<TTranslator extends BaseAcpTranslato
|
|
|
94
94
|
protected clearTurnState(): void;
|
|
95
95
|
protected releaseConnection(cwd: string): Promise<void>;
|
|
96
96
|
protected notifyIdle(cwd: string): void;
|
|
97
|
+
protected notifyConnectionActivity(cwd: string): void;
|
|
98
|
+
/** Wire the acquired client's hooks into the turn dispatcher (anchor-guarded). */
|
|
99
|
+
private installTurnHooks;
|
|
97
100
|
protected disposeConnections(): Promise<void>;
|
|
98
101
|
/**
|
|
99
102
|
* Respond to an approval server request. `response` is the bridge
|
|
@@ -65,7 +65,7 @@ export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
|
65
65
|
* 自身半死:opencode v2 的 location 在每轮拉起的 `opencode acp` 子进程里 boot,
|
|
66
66
|
* boot 期间后台服务不可用会让它长期停在未激活,后续轮次复用同一连接只会重复
|
|
67
67
|
* 失败——用户重启 opencode service 也治不好,因为连接缓存在本进程里
|
|
68
|
-
* (
|
|
68
|
+
* (4 次重试全失败,重启服务与 lark-remote 都无效)。丢掉连接
|
|
69
69
|
* 后下一轮重新 spawn,拿到的是新进程的新状态。
|
|
70
70
|
*
|
|
71
71
|
* 只对声明为可重试的错误生效:kimi 等沿用默认 `isRetryableSessionSetupError`
|
|
@@ -123,6 +123,21 @@ export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
|
123
123
|
notifyIdle(cwd) {
|
|
124
124
|
this.connectionManager.notifyIdle(cwd);
|
|
125
125
|
}
|
|
126
|
+
notifyConnectionActivity(cwd) {
|
|
127
|
+
this.connectionManager.notifyActivity(cwd);
|
|
128
|
+
}
|
|
129
|
+
/** Wire the acquired client's hooks into the turn dispatcher (anchor-guarded). */
|
|
130
|
+
installTurnHooks(anchor) {
|
|
131
|
+
anchor.client.setHooks({
|
|
132
|
+
onNotification: this.forCurrentTurn(anchor, 'notification', (method, params) => this.handleNotification(method, params)),
|
|
133
|
+
onServerRequest: this.forCurrentTurn(anchor, 'server request', (id, method, params) => this.handleServerRequest(id, method, params)),
|
|
134
|
+
onClose: this.forCurrentTurn(anchor, 'connection close', () => {
|
|
135
|
+
getLogger().warn(`[${this.logTag}] client connection closed`);
|
|
136
|
+
this.onConnectionClosed();
|
|
137
|
+
this.failTurn(this.connectionClosedMessage);
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
126
141
|
async disposeConnections() {
|
|
127
142
|
await this.beforeDisposeConnections();
|
|
128
143
|
await this.connectionManager.disposeAll();
|
|
@@ -201,36 +216,12 @@ export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
|
201
216
|
* session/resume (existing sessionId) → applyTurnSettings → session/prompt.
|
|
202
217
|
*/
|
|
203
218
|
async setupTurn(message, opts) {
|
|
204
|
-
//
|
|
205
|
-
// 结果/通知在新轮开始时 stopRequested
|
|
206
|
-
const
|
|
207
|
-
const client = await this.connectionManager.acquire(opts.cwd);
|
|
208
|
-
this.connectionManager.notifyActivity(opts.cwd);
|
|
209
|
-
this.currentClient = client;
|
|
219
|
+
// 本轮回调归属锚点(代际 + client)由 acquireForTurn 捕获:旧轮迟到的
|
|
220
|
+
// prompt 结果/通知在新轮开始时 stopRequested 已被重置,只有这对值能识别它。
|
|
221
|
+
const anchor = await this.acquireForTurn(opts.cwd, () => this.connectionManager.acquire(opts.cwd));
|
|
210
222
|
this.onConnectionAcquired(opts);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (!this.isCurrentTurn(generation, client)) {
|
|
214
|
-
getLogger().debug(`[${this.logTag}] dropped notification from a finished turn`);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
this.handleNotification(method, params);
|
|
218
|
-
},
|
|
219
|
-
onServerRequest: (id, method, params) => {
|
|
220
|
-
if (!this.isCurrentTurn(generation, client)) {
|
|
221
|
-
getLogger().debug(`[${this.logTag}] dropped server request from a finished turn`);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this.handleServerRequest(id, method, params);
|
|
225
|
-
},
|
|
226
|
-
onClose: () => {
|
|
227
|
-
if (!this.isCurrentTurn(generation, client))
|
|
228
|
-
return;
|
|
229
|
-
getLogger().warn(`[${this.logTag}] client connection closed`);
|
|
230
|
-
this.onConnectionClosed();
|
|
231
|
-
this.failTurn(this.connectionClosedMessage);
|
|
232
|
-
},
|
|
233
|
-
});
|
|
223
|
+
this.installTurnHooks(anchor);
|
|
224
|
+
const { client } = anchor;
|
|
234
225
|
let sessionId;
|
|
235
226
|
if (opts.sessionId) {
|
|
236
227
|
const resumeParams = {
|
|
@@ -267,19 +258,7 @@ export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
|
267
258
|
this.promptSettled = false;
|
|
268
259
|
const promptPromise = client.request('session/prompt', promptParams, Number.POSITIVE_INFINITY);
|
|
269
260
|
this.promptSent = true;
|
|
270
|
-
promptPromise.then((result) =>
|
|
271
|
-
if (!this.isCurrentTurn(generation, client)) {
|
|
272
|
-
getLogger().debug(`[${this.logTag}] dropped late prompt result from a finished turn`);
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
this.handlePromptResolved(result, translator, sessionId);
|
|
276
|
-
}, (err) => {
|
|
277
|
-
if (!this.isCurrentTurn(generation, client)) {
|
|
278
|
-
getLogger().debug(`[${this.logTag}] dropped late prompt rejection from a finished turn`);
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
this.handlePromptRejected(err, translator, sessionId, '');
|
|
282
|
-
});
|
|
261
|
+
promptPromise.then(this.forCurrentTurn(anchor, 'late prompt result', (result) => this.handlePromptResolved(result, translator, sessionId)), this.forCurrentTurn(anchor, 'late prompt rejection', (err) => this.handlePromptRejected(err, translator, sessionId, '')));
|
|
283
262
|
}
|
|
284
263
|
/**
|
|
285
264
|
* Run a compact operation on the current session: resume → '/compact'
|
|
@@ -287,29 +266,11 @@ export class BaseAcpRunner extends ConnectionBasedRunner {
|
|
|
287
266
|
*/
|
|
288
267
|
async *runCompact(_message, opts) {
|
|
289
268
|
yield* this.executeTurn(opts, async () => {
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
this.connectionManager.notifyActivity(opts.cwd);
|
|
293
|
-
this.currentClient = client;
|
|
269
|
+
// 同 setupTurn:acquireForTurn 捕获代际锚点,installTurnHooks 守卫 dispatch。
|
|
270
|
+
const anchor = await this.acquireForTurn(opts.cwd, () => this.connectionManager.acquire(opts.cwd));
|
|
294
271
|
this.onConnectionAcquired(opts);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (!this.isCurrentTurn(generation, client))
|
|
298
|
-
return;
|
|
299
|
-
this.handleNotification(method, params);
|
|
300
|
-
},
|
|
301
|
-
onServerRequest: (id, method, params) => {
|
|
302
|
-
if (!this.isCurrentTurn(generation, client))
|
|
303
|
-
return;
|
|
304
|
-
this.handleServerRequest(id, method, params);
|
|
305
|
-
},
|
|
306
|
-
onClose: () => {
|
|
307
|
-
if (!this.isCurrentTurn(generation, client))
|
|
308
|
-
return;
|
|
309
|
-
this.onConnectionClosed();
|
|
310
|
-
this.failTurn(this.connectionClosedMessage);
|
|
311
|
-
},
|
|
312
|
-
});
|
|
272
|
+
this.installTurnHooks(anchor);
|
|
273
|
+
const { client } = anchor;
|
|
313
274
|
const sessionId = opts.sessionId;
|
|
314
275
|
if (!sessionId) {
|
|
315
276
|
throw new Error('compact requires a sessionId');
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Both agents' session/update notification envelopes, prompt-response
|
|
5
5
|
* stopReason mapping, tool_call/tool_call_update normalization, usage_update
|
|
6
6
|
* occupancy bookkeeping and self-produced turn_started events are
|
|
7
|
-
* line-for-line identical — extracted here in the
|
|
7
|
+
* line-for-line identical — extracted here in the round-2
|
|
8
8
|
* simplification. What genuinely diverges is the content channel:
|
|
9
9
|
* kimi accumulates wire deltas into turn_diff snapshots (snapshot semantics,
|
|
10
10
|
* see KimiAcpTranslator header), opencode forwards `assistant` deltas
|
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import type { AgentEvent, ApprovalRequestedEvent, ApprovalView, ResultEvent, TurnStartedEvent } from '../../types.js';
|
|
23
23
|
import { type AgentMessageChunkEvent, type AgentThoughtChunkEvent, type ToolCallEvent, type ToolCallUpdateEvent, type UsageUpdateEvent, type RequestPermissionParams, type AcpPlanEvent } from './protocol-types.js';
|
|
24
|
+
/**
|
|
25
|
+
* Plan entries → 「图标 + 标题」markdown 清单(每行一条,`\n` join)。
|
|
26
|
+
* ACP 与 codex 双抄收编(R5);subtitle 拼接、trim、空清单语义等差异留在
|
|
27
|
+
* 各自调用点。
|
|
28
|
+
*/
|
|
29
|
+
export declare function renderPlanMarkdown(entries: ReadonlyArray<{
|
|
30
|
+
title: string;
|
|
31
|
+
subtitle?: string;
|
|
32
|
+
status?: string;
|
|
33
|
+
}>): string;
|
|
24
34
|
/** Self-produced turn_started event (no wire equivalent — produced on prompt start). */
|
|
25
35
|
export type AcpTurnStartedEvent = TurnStartedEvent;
|
|
26
36
|
/**
|
|
@@ -58,7 +68,7 @@ export declare abstract class BaseAcpTranslator {
|
|
|
58
68
|
/**
|
|
59
69
|
* Latest streaming-args text per tool call (keyed by wire toolCallId).
|
|
60
70
|
* kimi ACP sends the permission request BEFORE tool.call.started (verified
|
|
61
|
-
*
|
|
71
|
+
* against kimi 0.42.0): the request_permission toolCall
|
|
62
72
|
* carries only title + a truncated summary in content, NO rawInput — the
|
|
63
73
|
* full args only ever reach the client as accumulated text in the
|
|
64
74
|
* tool_call/tool_call_update streaming deltas. Tracking them here lets the
|
|
@@ -12,6 +12,32 @@ const KIND_TO_TOOL = {
|
|
|
12
12
|
search: 'Grep',
|
|
13
13
|
fetch: 'WebFetch',
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Plan 条目 status → 渲染图标。未知/缺省 status 一律按 pending 渲染——
|
|
17
|
+
* 两处调用点(ACP handlePlan / codex handleTurnPlanUpdated)注释都点名防
|
|
18
|
+
* wire 枚举漂移,新增枚举值时只改这张表。
|
|
19
|
+
*/
|
|
20
|
+
const PLAN_STATUS_ICONS = {
|
|
21
|
+
completed: '✅',
|
|
22
|
+
in_progress: '🔄',
|
|
23
|
+
pending: '⬜',
|
|
24
|
+
error: '❌',
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Plan entries → 「图标 + 标题」markdown 清单(每行一条,`\n` join)。
|
|
28
|
+
* ACP 与 codex 双抄收编(R5);subtitle 拼接、trim、空清单语义等差异留在
|
|
29
|
+
* 各自调用点。
|
|
30
|
+
*/
|
|
31
|
+
export function renderPlanMarkdown(entries) {
|
|
32
|
+
return entries
|
|
33
|
+
.map((entry) => {
|
|
34
|
+
const status = entry.status && entry.status in PLAN_STATUS_ICONS
|
|
35
|
+
? entry.status
|
|
36
|
+
: 'pending';
|
|
37
|
+
return `${PLAN_STATUS_ICONS[status]} ${entry.title}${entry.subtitle ? ` — ${entry.subtitle}` : ''}`;
|
|
38
|
+
})
|
|
39
|
+
.join('\n');
|
|
40
|
+
}
|
|
15
41
|
/**
|
|
16
42
|
* Join the text payloads of a tool_call(/_update) content array
|
|
17
43
|
* (`[{type:'content', content:{type:'text', text}}]` entries). Returns
|
|
@@ -59,7 +85,7 @@ export class BaseAcpTranslator {
|
|
|
59
85
|
/**
|
|
60
86
|
* Latest streaming-args text per tool call (keyed by wire toolCallId).
|
|
61
87
|
* kimi ACP sends the permission request BEFORE tool.call.started (verified
|
|
62
|
-
*
|
|
88
|
+
* against kimi 0.42.0): the request_permission toolCall
|
|
63
89
|
* carries only title + a truncated summary in content, NO rawInput — the
|
|
64
90
|
* full args only ever reach the client as accumulated text in the
|
|
65
91
|
* tool_call/tool_call_update streaming deltas. Tracking them here lets the
|
|
@@ -213,16 +239,12 @@ export class BaseAcpTranslator {
|
|
|
213
239
|
* completed)。基类具体方法:kimi/opencode wire 结构相同,直接复用。
|
|
214
240
|
*/
|
|
215
241
|
handlePlan(event) {
|
|
216
|
-
const ICON = { completed: '✅', in_progress: '🔄', pending: '⬜', error: '❌' };
|
|
217
242
|
const entries = Array.isArray(event.entries) ? event.entries : [];
|
|
218
|
-
const markdown = entries
|
|
219
|
-
.map((e) => {
|
|
243
|
+
const markdown = renderPlanMarkdown(entries.map((e) => {
|
|
220
244
|
const entry = e;
|
|
221
|
-
|
|
222
|
-
return
|
|
223
|
-
})
|
|
224
|
-
.join('\n')
|
|
225
|
-
.trim();
|
|
245
|
+
// ACP 条目的正文字段是 content(codex 是 title),无 subtitle。
|
|
246
|
+
return { title: entry.content ?? '', status: entry.status };
|
|
247
|
+
})).trim();
|
|
226
248
|
if (!markdown)
|
|
227
249
|
return [];
|
|
228
250
|
return [{ type: 'plan', plan: markdown, timestamp: new Date().toISOString() }];
|
|
@@ -238,7 +238,7 @@ export interface PermissionOption {
|
|
|
238
238
|
export interface RequestPermissionParams {
|
|
239
239
|
sessionId: string;
|
|
240
240
|
/**
|
|
241
|
-
* Approval target. Divergence by server
|
|
241
|
+
* Approval target. Divergence by server:
|
|
242
242
|
* - kimi 0.42: {toolCallId, title: toolName, content: [truncated summary
|
|
243
243
|
* text]} — NO rawInput; the approval gate fires BEFORE tool.call.started,
|
|
244
244
|
* so the full args only exist in the pre-approval tool_call/_update
|
|
@@ -43,7 +43,7 @@ export const RpcErrorCode = {
|
|
|
43
43
|
// =============================================================================
|
|
44
44
|
// Notifications (server → client, via session/update)
|
|
45
45
|
// Shape source: kimi-code/packages/acp-server/src/events-map.ts
|
|
46
|
-
// (each constructor below) +
|
|
46
|
+
// (each constructor below) + verified against kimi 0.36.0
|
|
47
47
|
//
|
|
48
48
|
// REAL envelope: {sessionId, update: {sessionUpdate: '<kind>', ...}}.
|
|
49
49
|
// The discriminator field is `update.sessionUpdate` — there is NO nested
|
|
@@ -72,7 +72,7 @@ export const SessionEventType = {
|
|
|
72
72
|
// Server Requests (approval — reverse RPC)
|
|
73
73
|
// Shape source: kimi-code/packages/acp-server/src/approval.ts:28-29
|
|
74
74
|
// options: [{optionId, name, kind}]; optionId is opaque, echo back as-is
|
|
75
|
-
// +
|
|
75
|
+
// + verified against kimi 0.36.0: approve_once / approve_always / reject
|
|
76
76
|
// =============================================================================
|
|
77
77
|
export const ServerRequestMethod = {
|
|
78
78
|
REQUEST_PERMISSION: 'session/request_permission',
|
|
@@ -18,6 +18,18 @@ import type { AgentKind } from '../../agent-kinds.js';
|
|
|
18
18
|
import type { AgentSessionReader } from '../../session-contract.js';
|
|
19
19
|
import type { AgentEvent, AgentRunner, AgentStatusInfo, SpawnOptions } from '../types.js';
|
|
20
20
|
import type { AgentStopper } from '../../platform/agent-stopper.js';
|
|
21
|
+
/**
|
|
22
|
+
* 本轮回调的归属锚点(generation + client),由 {@link ConnectionBasedRunner.acquireForTurn}
|
|
23
|
+
* 捕获。两个值在整轮内不可变:旧轮迟到的回调(prompt 结果 / 通知 / onClose)在
|
|
24
|
+
* 新轮开始时 `stopRequested` 已被重置,只有这对值能识别它们——pi 的教训是旧 client
|
|
25
|
+
* 上残留的上一轮 onClose 闭包会把假错误推进新 turn 的共享队列(用户消息未发出即
|
|
26
|
+
* 丢失),迟到的 agent_settled 同理(新 turn 空转立即「成功」)。所有 hook 的事件
|
|
27
|
+
* 落地前必须过 {@link ConnectionBasedRunner.isCurrentTurn} 守卫。
|
|
28
|
+
*/
|
|
29
|
+
export interface TurnAnchor<TClient> {
|
|
30
|
+
readonly client: TClient;
|
|
31
|
+
readonly generation: number;
|
|
32
|
+
}
|
|
21
33
|
export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent> implements AgentRunner {
|
|
22
34
|
readonly kind: AgentKind;
|
|
23
35
|
readonly sessionReader: AgentSessionReader;
|
|
@@ -115,7 +127,7 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
|
|
|
115
127
|
/**
|
|
116
128
|
* Mark this runner as evicted from the bridge's runner cache while a run may
|
|
117
129
|
* still be holding/creating connections on it (queue-advance race during
|
|
118
|
-
* interruptCurrentRun's await window
|
|
130
|
+
* interruptCurrentRun's await window). The active run's finally
|
|
119
131
|
* disposes connections eagerly instead of arming the idle TTL timer —
|
|
120
132
|
* otherwise the agent server process lingers for up to 30 minutes holding
|
|
121
133
|
* its session write lock (codex: "thread already has an active writer").
|
|
@@ -145,6 +157,25 @@ export declare abstract class ConnectionBasedRunner<TClient, TEvent = AgentEvent
|
|
|
145
157
|
* server request 一律只记 debug,不得进入共享 notificationQueue。
|
|
146
158
|
*/
|
|
147
159
|
protected isCurrentTurn(generation: number, client: TClient | null): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Acquire the per-turn connection and capture the callback anchor
|
|
162
|
+
* ({@link TurnAnchor}): acquire → activity ping → currentClient → anchor.
|
|
163
|
+
* Shared preamble of every setupTurn / runCompact; the acquired anchor is
|
|
164
|
+
* what the turn's hooks (and prompt-settle callbacks) validate against.
|
|
165
|
+
*
|
|
166
|
+
* `acquire` is a closure so subclasses keep their own acquire signatures
|
|
167
|
+
* (pi passes the spawn-bound `{ sessionId }`).
|
|
168
|
+
*/
|
|
169
|
+
protected acquireForTurn(cwd: string, acquire: () => Promise<TClient>): Promise<TurnAnchor<TClient>>;
|
|
170
|
+
/** Per-cwd activity ping on the subclass connection manager (mirror of notifyIdle). */
|
|
171
|
+
protected abstract notifyConnectionActivity(cwd: string): void;
|
|
172
|
+
/**
|
|
173
|
+
* Wrap a per-turn callback with the isCurrentTurn guard: events from a
|
|
174
|
+
* finished turn are dropped with a debug log and never reach the turn
|
|
175
|
+
* dispatcher. 6 处守卫原有的日志粒度不一(ACP setupTurn 记 debug,其余静默),
|
|
176
|
+
* 统一为最详细版本:dropped 一律 debug,便于事后追溯。
|
|
177
|
+
*/
|
|
178
|
+
protected forCurrentTurn<A extends unknown[]>(anchor: TurnAnchor<TClient>, label: string, handler: (...args: A) => void): (...args: A) => void;
|
|
148
179
|
protected failTurn(message: string): void;
|
|
149
180
|
protected resetRunState(): void;
|
|
150
181
|
/** Release the connection + detach stale client hooks after a lost connection. */
|