lark-remote 0.5.13 → 0.5.14
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 +15 -0
- package/dist/agent-kinds.d.ts +27 -0
- package/dist/agent-kinds.js +38 -0
- package/dist/bridge/approval-actions.d.ts +95 -0
- package/dist/bridge/approval-actions.js +104 -0
- package/dist/bridge/final-usage.d.ts +8 -17
- package/dist/bridge/final-usage.js +1 -18
- package/dist/bridge/index.d.ts +46 -39
- package/dist/bridge/index.js +201 -229
- package/dist/bridge/queue-manager.d.ts +25 -1
- package/dist/bridge/queue-manager.js +179 -160
- package/dist/bridge/run-approval.d.ts +2 -1
- package/dist/bridge/run-usage.d.ts +16 -30
- package/dist/bridge/run-usage.js +41 -50
- package/dist/bridge/session-writeback.d.ts +3 -2
- package/dist/card/approval-render.d.ts +3 -2
- package/dist/card/approval-render.js +3 -2
- package/dist/{router → card}/card-helpers.d.ts +7 -7
- package/dist/{router → card}/card-helpers.js +8 -8
- package/dist/card/card-session.d.ts +4 -2
- package/dist/card/card-session.js +8 -6
- package/dist/card/card-shared.d.ts +1 -1
- package/dist/card/card-shared.js +1 -1
- package/dist/card/final-answer-overflow.d.ts +0 -6
- package/dist/card/final-answer-overflow.js +1 -1
- package/dist/card/run-card-session.d.ts +2 -2
- package/dist/card/run-card-session.js +2 -2
- package/dist/card/run-renderer.js +7 -22
- package/dist/card/run-state.d.ts +28 -50
- package/dist/card/run-state.js +23 -0
- package/dist/card/usage-format.d.ts +30 -0
- package/dist/{router → card}/usage-format.js +3 -10
- package/dist/clone.js +6 -1
- package/dist/config/index.d.ts +16 -20
- package/dist/config/index.js +0 -16
- package/dist/config/pi-config.js +6 -4
- package/dist/connector/index.d.ts +2 -4
- package/dist/connector/index.js +7 -11
- package/dist/{runner/dsh → dsh}/auth.js +1 -1
- package/dist/dsh/protocol.d.ts +9 -0
- package/dist/dsh/protocol.js +61 -0
- package/dist/index.js +10 -5
- package/dist/model-catalog.d.ts +14 -0
- package/dist/model-catalog.js +25 -0
- package/dist/platform/path.d.ts +9 -0
- package/dist/platform/path.js +18 -0
- package/dist/router/active-run-usage.d.ts +4 -0
- package/dist/router/active-run-usage.js +12 -0
- package/dist/router/commands/ls.js +1 -1
- package/dist/router/commands/order.js +1 -1
- package/dist/router/commands/resume.d.ts +5 -4
- package/dist/router/commands/resume.js +4 -4
- package/dist/router/commands/ws.js +1 -1
- package/dist/router/config/claude.js +2 -1
- package/dist/router/config/common/render.d.ts +5 -1
- package/dist/router/config/common/render.js +6 -13
- package/dist/router/config/dsh.d.ts +1 -1
- package/dist/router/config/index.d.ts +1 -1
- package/dist/router/config/types.d.ts +11 -3
- package/dist/router/index.d.ts +41 -11
- package/dist/router/index.js +165 -226
- package/dist/runner/agent-choices-common.d.ts +1 -1
- package/dist/runner/claude/runner.d.ts +20 -2
- package/dist/runner/claude/runner.js +2 -15
- package/dist/runner/codex/app-server/runner.d.ts +3 -1
- package/dist/runner/codex/app-server/translator.d.ts +11 -0
- package/dist/runner/codex/app-server/translator.js +56 -149
- package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
- package/dist/runner/common/connection-based-runner.d.ts +3 -1
- package/dist/runner/common/jsonrpc/transport.js +17 -7
- package/dist/runner/dsh/client.d.ts +1 -4
- package/dist/runner/dsh/client.js +2 -55
- package/dist/runner/dsh/daemon.js +9 -2
- package/dist/runner/dsh/exit-cancel.js +1 -1
- package/dist/runner/dsh/mux-stream.js +21 -2
- package/dist/runner/dsh/runner.d.ts +21 -1
- package/dist/runner/dsh/runner.js +76 -73
- package/dist/runner/dsh/translator.d.ts +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +22 -1
- package/dist/runner/kimi/acp/runner.js +83 -60
- package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
- package/dist/runner/opencode/acp/error-monitor.js +31 -2
- package/dist/runner/opencode/acp/runner.d.ts +3 -1
- package/dist/runner/pi/rpc/runner.d.ts +2 -1
- package/dist/runner/probe.d.ts +1 -1
- package/dist/runner/probe.js +10 -9
- package/dist/runner/registry.d.ts +2 -1
- package/dist/runner/types.d.ts +20 -154
- package/dist/runner/types.js +4 -24
- package/dist/session/claude/session-index.d.ts +11 -4
- package/dist/session/claude/session-index.js +30 -8
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/sessions.d.ts +2 -2
- package/dist/session/claude/sessions.js +41 -26
- package/dist/session/codex/rollout-reader.d.ts +1 -1
- package/dist/session/codex/rollout-reader.js +14 -13
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +4 -2
- package/dist/session/common/jsonl.js +7 -1
- package/dist/session/common/pagination.d.ts +2 -0
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/common/recency.d.ts +6 -0
- package/dist/session/common/recency.js +7 -5
- package/dist/session/common/two-pass.d.ts +1 -1
- package/dist/session/common/usage-accumulator.d.ts +1 -1
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +37 -12
- package/dist/session/kimi/sessions.d.ts +1 -1
- package/dist/session/kimi/sessions.js +35 -8
- package/dist/session/opencode/sessions.d.ts +1 -1
- package/dist/session/opencode/sessions.js +51 -47
- package/dist/session/pi/sessions.d.ts +1 -1
- package/dist/session/pi/sessions.js +20 -7
- package/dist/session/registry.d.ts +2 -1
- package/dist/session/session-store.d.ts +1 -2
- package/dist/session/session-store.js +14 -7
- package/dist/session-contract.d.ts +178 -0
- package/dist/session-contract.js +9 -0
- package/package.json +1 -1
- package/dist/router/usage-format.d.ts +0 -60
- /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/home.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/home.js +0 -0
- /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.js +0 -0
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { getLogger } from '../../logger/index.js';
|
|
2
2
|
import { DshError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* onerror 的事件对象在 ws 实现之间形态不一({message} / {error} / {type})。
|
|
5
|
+
* 尽力取出可读描述;取不到就不追加,别拿 '[object Object]' 充数。
|
|
6
|
+
*/
|
|
7
|
+
function describeSocketError(event) {
|
|
8
|
+
if (!event || typeof event !== 'object')
|
|
9
|
+
return undefined;
|
|
10
|
+
const e = event;
|
|
11
|
+
if (typeof e.message === 'string' && e.message)
|
|
12
|
+
return e.message;
|
|
13
|
+
if (e.error instanceof Error && e.error.message)
|
|
14
|
+
return e.error.message;
|
|
15
|
+
if (typeof e.error === 'string' && e.error)
|
|
16
|
+
return e.error;
|
|
17
|
+
if (typeof e.type === 'string' && e.type)
|
|
18
|
+
return e.type;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
3
21
|
export async function* openMuxStream(opts) {
|
|
4
22
|
const cookie = await opts.authCookie();
|
|
5
23
|
const socket = opts.webSocketFactory(`${opts.baseUrl.replace(/^http/, 'ws')}/api/remote.mux`, cookie ? { headers: { cookie } } : undefined);
|
|
@@ -87,10 +105,11 @@ export async function* openMuxStream(opts) {
|
|
|
87
105
|
settle(undefined);
|
|
88
106
|
}
|
|
89
107
|
};
|
|
90
|
-
socket.onerror = () => {
|
|
108
|
+
socket.onerror = (event) => {
|
|
91
109
|
if (ended)
|
|
92
110
|
return;
|
|
93
|
-
|
|
111
|
+
const detail = describeSocketError(event);
|
|
112
|
+
failure = new DshError('transport', `DSH remote mux WebSocket error${detail ? `: ${detail}` : ''}`);
|
|
94
113
|
ended = true;
|
|
95
114
|
settle(undefined);
|
|
96
115
|
};
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* approval on the same card. If `$events` is unavailable, the runner falls
|
|
18
18
|
* back to the Web UI prompt.
|
|
19
19
|
*/
|
|
20
|
-
import type {
|
|
20
|
+
import type { AgentSessionReader } from '../../session-contract.js';
|
|
21
|
+
import type { AgentEvent, AgentStatusInfo, SpawnOptions, StopOutcome } from '../types.js';
|
|
21
22
|
import type { AgentRunner } from '../types.js';
|
|
22
23
|
import { DshClient, type WebSocketFactory } from './client.js';
|
|
23
24
|
import type { DshDaemon } from './daemon.js';
|
|
@@ -134,6 +135,25 @@ export declare class DshRunner implements AgentRunner {
|
|
|
134
135
|
* the live catalog for that provider's default (or first) model.
|
|
135
136
|
*/
|
|
136
137
|
private resolveModelSelection;
|
|
138
|
+
/**
|
|
139
|
+
* 新建 DSH session 并取回 sessionId;run 在挂起期间被 stop 时返回 undefined。
|
|
140
|
+
*
|
|
141
|
+
* 红线:不把 run 的 abort signal 直接交给 create —— abort 掉请求会丢掉刚创建的
|
|
142
|
+
* sessionId,服务端若已完成创建就留下一个无法 cancel 的孤儿 session。改为 abort
|
|
143
|
+
* 时立即放行 run(队列不被挂起),create 后台落定后再补 cancel。
|
|
144
|
+
*/
|
|
145
|
+
private acquireSessionId;
|
|
146
|
+
/**
|
|
147
|
+
* 把本轮消息交给 DSH 的排队收件箱,返回本轮的身份与起始水位。
|
|
148
|
+
*
|
|
149
|
+
* 三条不变量(都关系到「迟到/抢先的 stop 能否被正确撤销」):
|
|
150
|
+
* - 起始水位取 prompt **之前**的 latestEventSeq:session/page 返回整个会话,
|
|
151
|
+
* 从 0 开始会把首轮已完成 turn 在每次后续消息时重放一遍。
|
|
152
|
+
* - 身份(requestId)在请求发出**之前**就公布:stop 可能落在 session/prompt 的
|
|
153
|
+
* 在途窗口里,那时它必须知道宿主受理后该撤销哪个收件项。
|
|
154
|
+
* - `promptAdmitted` 只在宿主接受后置真:撤销路径据此区分「还没进去」与「已排队」。
|
|
155
|
+
*/
|
|
156
|
+
private submitPrompt;
|
|
137
157
|
run(message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
|
|
138
158
|
/**
|
|
139
159
|
* Consume one turn from both transports. `session/follow` owns transcript
|
|
@@ -178,6 +178,20 @@ function parseToolArguments(raw) {
|
|
|
178
178
|
return {};
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* 用户 stop 的终态结果(run() 的 5 个中止点各 yield 同一形状)。
|
|
183
|
+
*
|
|
184
|
+
* 必须 yield result 而不是裸 return:调用方靠 result 收尾 run 卡,否则卡片
|
|
185
|
+
* 停留在「运行中」(见 run() 内 startup abort 注释)。
|
|
186
|
+
*/
|
|
187
|
+
function interruptedResult(sessionId) {
|
|
188
|
+
return {
|
|
189
|
+
type: 'result',
|
|
190
|
+
subtype: 'interrupted',
|
|
191
|
+
session_id: sessionId,
|
|
192
|
+
errorMessage: 'run stopped',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
181
195
|
export class DshRunner {
|
|
182
196
|
kind = 'dsh';
|
|
183
197
|
/**
|
|
@@ -323,6 +337,57 @@ export class DshRunner {
|
|
|
323
337
|
}
|
|
324
338
|
return { provider, model, ...(reasoningEffort ? { reasoningEffort } : {}) };
|
|
325
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* 新建 DSH session 并取回 sessionId;run 在挂起期间被 stop 时返回 undefined。
|
|
342
|
+
*
|
|
343
|
+
* 红线:不把 run 的 abort signal 直接交给 create —— abort 掉请求会丢掉刚创建的
|
|
344
|
+
* sessionId,服务端若已完成创建就留下一个无法 cancel 的孤儿 session。改为 abort
|
|
345
|
+
* 时立即放行 run(队列不被挂起),create 后台落定后再补 cancel。
|
|
346
|
+
*/
|
|
347
|
+
async acquireSessionId(cwd, signal) {
|
|
348
|
+
const createPromise = this.client.createSession({
|
|
349
|
+
cwd,
|
|
350
|
+
...(this.agentPreset ? { agentPreset: this.agentPreset } : {}),
|
|
351
|
+
});
|
|
352
|
+
const created = await this.awaitOrAbort(createPromise, signal);
|
|
353
|
+
if (created)
|
|
354
|
+
return created.sessionId;
|
|
355
|
+
void createPromise.then((c) => this.cancelSessionQuietly(c.sessionId), () => {
|
|
356
|
+
/* create failed after stop — nothing to cancel */
|
|
357
|
+
});
|
|
358
|
+
getLogger().info('[dsh] run stopped during session/create; will cancel if it settles');
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* 把本轮消息交给 DSH 的排队收件箱,返回本轮的身份与起始水位。
|
|
363
|
+
*
|
|
364
|
+
* 三条不变量(都关系到「迟到/抢先的 stop 能否被正确撤销」):
|
|
365
|
+
* - 起始水位取 prompt **之前**的 latestEventSeq:session/page 返回整个会话,
|
|
366
|
+
* 从 0 开始会把首轮已完成 turn 在每次后续消息时重放一遍。
|
|
367
|
+
* - 身份(requestId)在请求发出**之前**就公布:stop 可能落在 session/prompt 的
|
|
368
|
+
* 在途窗口里,那时它必须知道宿主受理后该撤销哪个收件项。
|
|
369
|
+
* - `promptAdmitted` 只在宿主接受后置真:撤销路径据此区分「还没进去」与「已排队」。
|
|
370
|
+
*/
|
|
371
|
+
async submitPrompt(sessionId, cwd, message, signal) {
|
|
372
|
+
const prePromptSeq = await this.client.latestEventSeq(sessionId, { signal });
|
|
373
|
+
const promptRequestId = newPromptRequestId();
|
|
374
|
+
this.activePromptRequestId = promptRequestId;
|
|
375
|
+
this.promptAdmitted = false;
|
|
376
|
+
this.inflight?.record({
|
|
377
|
+
sessionId,
|
|
378
|
+
promptRequestId,
|
|
379
|
+
baseUrl: this.client.baseUrl,
|
|
380
|
+
cwd,
|
|
381
|
+
});
|
|
382
|
+
await this.client.unary('session/prompt', {
|
|
383
|
+
requestId: promptRequestId,
|
|
384
|
+
sessionId,
|
|
385
|
+
mode: 'queue',
|
|
386
|
+
content: [{ type: 'text', text: message }],
|
|
387
|
+
}, { signal });
|
|
388
|
+
this.promptAdmitted = true;
|
|
389
|
+
return { prePromptSeq, promptRequestId };
|
|
390
|
+
}
|
|
326
391
|
async *run(message, opts) {
|
|
327
392
|
if (this.running) {
|
|
328
393
|
throw new Error('DshRunner is already running');
|
|
@@ -346,29 +411,13 @@ export class DshRunner {
|
|
|
346
411
|
// session/create 必须包进同一 try/finally,否则创建失败(服务端不可达/报错)
|
|
347
412
|
// 会在进入旧 try 之前抛异常,finally 不执行 → running 恒 true、activeAbort 残留。
|
|
348
413
|
if (!sessionId) {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
...(this.agentPreset ? { agentPreset: this.agentPreset } : {}),
|
|
352
|
-
});
|
|
353
|
-
// 不把 run 的 abort signal 直接交给 create:abort 掉请求会丢掉刚创建
|
|
354
|
-
// 的 sessionId,服务端若已完成创建就留下一个无法 cancel 的孤儿 session。
|
|
355
|
-
// 改为 abort 时立即放行 run(队列不被挂起),create 后台落定后再补 cancel。
|
|
356
|
-
const created = await this.awaitOrAbort(createPromise, abort.signal);
|
|
357
|
-
if (!created) {
|
|
358
|
-
void createPromise.then((c) => this.cancelSessionQuietly(c.sessionId), () => {
|
|
359
|
-
/* create failed after stop — nothing to cancel */
|
|
360
|
-
});
|
|
361
|
-
getLogger().info('[dsh] run stopped during session/create; will cancel if it settles');
|
|
414
|
+
const created = await this.acquireSessionId(opts.cwd, abort.signal);
|
|
415
|
+
if (created === undefined) {
|
|
362
416
|
terminalEmitted = true;
|
|
363
|
-
yield
|
|
364
|
-
type: 'result',
|
|
365
|
-
subtype: 'interrupted',
|
|
366
|
-
session_id: '',
|
|
367
|
-
errorMessage: 'run stopped',
|
|
368
|
-
};
|
|
417
|
+
yield interruptedResult('');
|
|
369
418
|
return;
|
|
370
419
|
}
|
|
371
|
-
sessionId = created
|
|
420
|
+
sessionId = created;
|
|
372
421
|
}
|
|
373
422
|
this.activeSessionId = sessionId;
|
|
374
423
|
// Startup abort:createSession 挂起期间 stop() 已 abort(此时 sessionId 尚
|
|
@@ -380,12 +429,7 @@ export class DshRunner {
|
|
|
380
429
|
await this.cancelSessionQuietly(sessionId);
|
|
381
430
|
getLogger().info(`[dsh] run aborted during startup, cancelled session ${sessionId}`);
|
|
382
431
|
terminalEmitted = true;
|
|
383
|
-
yield
|
|
384
|
-
type: 'result',
|
|
385
|
-
subtype: 'interrupted',
|
|
386
|
-
session_id: sessionId,
|
|
387
|
-
errorMessage: 'run stopped',
|
|
388
|
-
};
|
|
432
|
+
yield interruptedResult(sessionId);
|
|
389
433
|
return;
|
|
390
434
|
}
|
|
391
435
|
// provider/模型/推理强度对齐:会话首次 run 时 selectModel 一次
|
|
@@ -403,12 +447,7 @@ export class DshRunner {
|
|
|
403
447
|
if (abort.signal.aborted) {
|
|
404
448
|
getLogger().info(`[dsh] run stopped during selectModel session=${sessionId}`);
|
|
405
449
|
terminalEmitted = true;
|
|
406
|
-
yield
|
|
407
|
-
type: 'result',
|
|
408
|
-
subtype: 'interrupted',
|
|
409
|
-
session_id: sessionId,
|
|
410
|
-
errorMessage: 'run stopped',
|
|
411
|
-
};
|
|
450
|
+
yield interruptedResult(sessionId);
|
|
412
451
|
return;
|
|
413
452
|
}
|
|
414
453
|
if (this.configuredProvider) {
|
|
@@ -425,12 +464,7 @@ export class DshRunner {
|
|
|
425
464
|
// Abort 可能在上述 unary 完成与检查之间到达:不进入 prompt/turn。
|
|
426
465
|
if (abort.signal.aborted) {
|
|
427
466
|
terminalEmitted = true;
|
|
428
|
-
yield
|
|
429
|
-
type: 'result',
|
|
430
|
-
subtype: 'interrupted',
|
|
431
|
-
session_id: sessionId,
|
|
432
|
-
errorMessage: 'run stopped',
|
|
433
|
-
};
|
|
467
|
+
yield interruptedResult(sessionId);
|
|
434
468
|
return;
|
|
435
469
|
}
|
|
436
470
|
yield {
|
|
@@ -453,48 +487,17 @@ export class DshRunner {
|
|
|
453
487
|
if (seedGoal.known && seedGoal.goal)
|
|
454
488
|
yield goalStateEvent(seedGoal.goal, true);
|
|
455
489
|
try {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// would replay the first completed turn on every follow-up prompt.
|
|
459
|
-
const prePromptSeq = await this.client.latestEventSeq(sessionId, {
|
|
460
|
-
signal: abort.signal,
|
|
461
|
-
});
|
|
462
|
-
// Keep the prompt identity: DSH echoes it into the durable user/message
|
|
463
|
-
// source.rpcId, which is how runTurn tells this run's turn from a turn
|
|
464
|
-
// it is merely queued behind.
|
|
465
|
-
promptRequestId = newPromptRequestId();
|
|
466
|
-
// Publish the identity before the request leaves: a stop that arrives
|
|
467
|
-
// while `session/prompt` is in flight must still know which inbox item
|
|
468
|
-
// (if any) to withdraw once the host admits it.
|
|
469
|
-
this.activePromptRequestId = promptRequestId;
|
|
470
|
-
this.promptAdmitted = false;
|
|
471
|
-
this.inflight?.record({
|
|
472
|
-
sessionId,
|
|
473
|
-
promptRequestId,
|
|
474
|
-
baseUrl: this.client.baseUrl,
|
|
475
|
-
cwd: opts.cwd,
|
|
476
|
-
});
|
|
477
|
-
await this.client.unary('session/prompt', {
|
|
478
|
-
requestId: promptRequestId,
|
|
479
|
-
sessionId,
|
|
480
|
-
mode: 'queue',
|
|
481
|
-
content: [{ type: 'text', text: message }],
|
|
482
|
-
}, { signal: abort.signal });
|
|
483
|
-
this.promptAdmitted = true;
|
|
490
|
+
const submitted = await this.submitPrompt(sessionId, opts.cwd, message, abort.signal);
|
|
491
|
+
promptRequestId = submitted.promptRequestId;
|
|
484
492
|
if (abort.signal.aborted) {
|
|
485
493
|
// stop() 在 prompt 已受理后到达:cancel 由 stop() 发出,这里直接收尾,
|
|
486
494
|
// 不再打开 mux(否则会看到 cancel 产生的 turn/end 事件)。
|
|
487
495
|
terminalEmitted = true;
|
|
488
|
-
yield
|
|
489
|
-
type: 'result',
|
|
490
|
-
subtype: 'interrupted',
|
|
491
|
-
session_id: sessionId,
|
|
492
|
-
errorMessage: 'run stopped',
|
|
493
|
-
};
|
|
496
|
+
yield interruptedResult(sessionId);
|
|
494
497
|
return;
|
|
495
498
|
}
|
|
496
499
|
try {
|
|
497
|
-
for await (const ev of this.runTurn(sessionId, opts.cwd, prePromptSeq, promptRequestId, abort, { last: lastGoal })) {
|
|
500
|
+
for await (const ev of this.runTurn(sessionId, opts.cwd, submitted.prePromptSeq, promptRequestId, abort, { last: lastGoal })) {
|
|
498
501
|
yield ev;
|
|
499
502
|
if (ev.type === 'result') {
|
|
500
503
|
terminalEmitted = true;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* cache_creation_tokens (cacheWrite = cache creation) + total_tokens sum.
|
|
25
25
|
*/
|
|
26
26
|
import type { AgentEvent, ResultEvent } from '../types.js';
|
|
27
|
-
import type { DshAssistantStreamFrame, DshSessionEvent, DshTokenUsage } from '
|
|
27
|
+
import type { DshAssistantStreamFrame, DshSessionEvent, DshTokenUsage } from '../../dsh/types.js';
|
|
28
28
|
export declare class DshTranslator {
|
|
29
29
|
/** 本 turn 累计用量:DSH 每次模型调用(step)一条 usage,工具往返 = 多条。 */
|
|
30
30
|
private turnUsage;
|
package/dist/runner/index.d.ts
CHANGED
|
@@ -15,6 +15,6 @@ export { KimiAcpRunner } from './kimi/index.js';
|
|
|
15
15
|
export { DshRunner } from './dsh/index.js';
|
|
16
16
|
export { BashProcessRunner, type BashRunner } from './bash/index.js';
|
|
17
17
|
export { AgentRegistry } from './registry.js';
|
|
18
|
-
export type { ResultEvent, PlanEvent, FileChangeEvent, AgentEvent, Runner, RunnerLifetime,
|
|
18
|
+
export type { ResultEvent, PlanEvent, FileChangeEvent, AgentEvent, Runner, RunnerLifetime, AgentRunner, TurnStartedEvent, TurnDiffEvent, FinalAnswerEvent, NoticeEvent, SessionInfoEvent, ToolHint, ApprovalView, ApprovalRequestedEvent, ApprovalResolvedEvent, GoalView, GoalStateEvent, } from './types.js';
|
|
19
19
|
export { resolveAgentChoices } from './resolve-agent-choices.js';
|
|
20
20
|
export { syncAgentChoices } from './sync-agent-choices.js';
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* - AskUserQuestion flows through elicitation forms + a request_permission
|
|
18
18
|
* question bridge (buildQuestionResponse)
|
|
19
19
|
*/
|
|
20
|
-
import type { AgentKind
|
|
20
|
+
import type { AgentKind } from '../../../agent-kinds.js';
|
|
21
|
+
import type { AgentSessionReader } from '../../../session-contract.js';
|
|
22
|
+
import type { AgentStatusInfo, SpawnOptions } from '../../types.js';
|
|
21
23
|
import { JsonRpcClient } from '../../common/jsonrpc/client.js';
|
|
22
24
|
import { KimiAcpTranslator } from './translator.js';
|
|
23
25
|
import { type SessionPromptResult } from '../../common/acp/protocol-types.js';
|
|
@@ -167,6 +169,25 @@ export declare class KimiAcpRunner extends BaseAcpRunner<KimiAcpTranslator> {
|
|
|
167
169
|
* stdout(+stderr) 到缓冲,立即回 {terminalId}。
|
|
168
170
|
*/
|
|
169
171
|
private handleTerminalCreate;
|
|
172
|
+
/**
|
|
173
|
+
* 走 shell seam:argv 逐 token 保留,只把「可执行文件在哪」收口——win32 上
|
|
174
|
+
* Git Bash 缺失时在这里抛 ShellUnavailableError,由 handleTerminalRequest 转成
|
|
175
|
+
* 明确的 Terminal request failed 回执,而不是一个语焉不详的 spawn ENOENT。
|
|
176
|
+
*/
|
|
177
|
+
private spawnTerminalProcess;
|
|
178
|
+
/**
|
|
179
|
+
* 有界输出缓冲:每个流独立 decoder(多字节字符跨 chunk 拆分时由 decoder 保留
|
|
180
|
+
* 不完整序列,避免按 chunk 单独 toString 产生 U+FFFD 替换符)。
|
|
181
|
+
*/
|
|
182
|
+
private attachTerminalOutputBuffer;
|
|
183
|
+
/**
|
|
184
|
+
* 退出/错误收尾管线:tool_result 必须先入队,再放行 `wait_for_exit`。
|
|
185
|
+
*/
|
|
186
|
+
private wireTerminalExit;
|
|
187
|
+
/** Bash 面板的 tool_result 帧(输出全量 + 是否失败)。 */
|
|
188
|
+
private pushTerminalToolResult;
|
|
189
|
+
/** Bash 面板的 tool_use 帧(真实命令 + cwd,create 时立刻可见)。 */
|
|
190
|
+
private pushTerminalToolUse;
|
|
170
191
|
/** terminal/output: 回累计全量输出 + truncated + 当前退出状态(可未退出)。 */
|
|
171
192
|
private handleTerminalOutput;
|
|
172
193
|
/** terminal/wait_for_exit: 未退出则等 exit promise 再回 {exitCode, signal}。 */
|
|
@@ -582,11 +582,55 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
582
582
|
: undefined;
|
|
583
583
|
const cwd = params.cwd ?? this.activeCwd ?? undefined;
|
|
584
584
|
const outputLimit = params.outputByteLimit ?? TERMINAL_DEFAULT_OUTPUT_LIMIT;
|
|
585
|
-
|
|
586
|
-
//
|
|
587
|
-
//
|
|
588
|
-
//
|
|
589
|
-
|
|
585
|
+
const proc = this.spawnTerminalProcess(params, cwd, env);
|
|
586
|
+
// spawn 失败(如 ENOENT)只发 'error' 不发 'exit'——一并 resolve,防止
|
|
587
|
+
// wait_for_exit 永久挂起(runner 层 ENOENT 兜底红线)。
|
|
588
|
+
// 用 resolveExit 承接,避免回调在 handle 初始化前引用它(TDZ)。
|
|
589
|
+
let resolveExit;
|
|
590
|
+
const exitPromise = new Promise((resolve) => {
|
|
591
|
+
resolveExit = resolve;
|
|
592
|
+
});
|
|
593
|
+
// 与 resolveExit 同款 TDZ 规避:tool_result 发射闭包在 terminalId 初始化
|
|
594
|
+
// 后赋值,exit/error 回调执行时经 emitResult 间接调用。
|
|
595
|
+
let emitTerminalResult = undefined;
|
|
596
|
+
const emitResult = () => {
|
|
597
|
+
emitTerminalResult?.();
|
|
598
|
+
};
|
|
599
|
+
const handle = {
|
|
600
|
+
proc,
|
|
601
|
+
output: '',
|
|
602
|
+
outputBytes: 0,
|
|
603
|
+
outputLimit,
|
|
604
|
+
truncated: false,
|
|
605
|
+
exitCode: null,
|
|
606
|
+
signal: null,
|
|
607
|
+
exitPromise,
|
|
608
|
+
};
|
|
609
|
+
this.attachTerminalOutputBuffer(proc, handle);
|
|
610
|
+
this.wireTerminalExit(proc, handle, emitResult, () => resolveExit?.());
|
|
611
|
+
const terminalId = `term-${++this.terminalSeq}`;
|
|
612
|
+
this.terminals.set(terminalId, handle);
|
|
613
|
+
// Bash 工具事件由本 runner 自产(kimi 通知已过滤):create 即推 tool_use
|
|
614
|
+
// 带真实命令,让 run 卡立刻出现可识别的 Bash 面板。
|
|
615
|
+
const script = Array.isArray(params.args) && params.args.length > 1 ? params.args[1] : params.command;
|
|
616
|
+
let resultEmitted = false;
|
|
617
|
+
emitTerminalResult = () => {
|
|
618
|
+
if (resultEmitted)
|
|
619
|
+
return;
|
|
620
|
+
resultEmitted = true;
|
|
621
|
+
this.pushTerminalToolResult(terminalId, handle.output, handle.exitCode);
|
|
622
|
+
};
|
|
623
|
+
this.pushTerminalToolUse(terminalId, script, cwd);
|
|
624
|
+
getLogger().info(`[${this.logTag}] terminal created id=${terminalId} command=${params.command} args=${JSON.stringify(params.args ?? [])}`);
|
|
625
|
+
this.currentClient?.respond(id, { terminalId });
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* 走 shell seam:argv 逐 token 保留,只把「可执行文件在哪」收口——win32 上
|
|
629
|
+
* Git Bash 缺失时在这里抛 ShellUnavailableError,由 handleTerminalRequest 转成
|
|
630
|
+
* 明确的 Terminal request failed 回执,而不是一个语焉不详的 spawn ENOENT。
|
|
631
|
+
*/
|
|
632
|
+
spawnTerminalProcess(params, cwd, env) {
|
|
633
|
+
return this.shell.spawnArgv(params.command, params.args ?? [], {
|
|
590
634
|
cwd,
|
|
591
635
|
options: {
|
|
592
636
|
// env 覆盖大小写不敏感合并(win32 PATH/Path 双键防护)
|
|
@@ -600,8 +644,12 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
600
644
|
windowsHide: true,
|
|
601
645
|
},
|
|
602
646
|
});
|
|
603
|
-
|
|
604
|
-
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* 有界输出缓冲:每个流独立 decoder(多字节字符跨 chunk 拆分时由 decoder 保留
|
|
650
|
+
* 不完整序列,避免按 chunk 单独 toString 产生 U+FFFD 替换符)。
|
|
651
|
+
*/
|
|
652
|
+
attachTerminalOutputBuffer(proc, handle) {
|
|
605
653
|
const stdoutDecoder = new StringDecoder('utf8');
|
|
606
654
|
const stderrDecoder = new StringDecoder('utf8');
|
|
607
655
|
const append = (chunk, decoder) => {
|
|
@@ -624,29 +672,11 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
624
672
|
};
|
|
625
673
|
proc.stdout?.on('data', (chunk) => append(chunk, stdoutDecoder));
|
|
626
674
|
proc.stderr?.on('data', (chunk) => append(chunk, stderrDecoder));
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
resolveExit = resolve;
|
|
633
|
-
});
|
|
634
|
-
// 与 resolveExit 同款 TDZ 规避:tool_result 发射闭包在 terminalId 初始化
|
|
635
|
-
// 后赋值,exit/error 回调执行时经 emitResult 间接调用。
|
|
636
|
-
let emitTerminalResult = undefined;
|
|
637
|
-
const emitResult = () => {
|
|
638
|
-
emitTerminalResult?.();
|
|
639
|
-
};
|
|
640
|
-
const handle = {
|
|
641
|
-
proc,
|
|
642
|
-
output: '',
|
|
643
|
-
outputBytes: 0,
|
|
644
|
-
outputLimit,
|
|
645
|
-
truncated: false,
|
|
646
|
-
exitCode: null,
|
|
647
|
-
signal: null,
|
|
648
|
-
exitPromise,
|
|
649
|
-
};
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* 退出/错误收尾管线:tool_result 必须先入队,再放行 `wait_for_exit`。
|
|
678
|
+
*/
|
|
679
|
+
wireTerminalExit(proc, handle, emitResult, resolveExit) {
|
|
650
680
|
proc.once('exit', (code, signal) => {
|
|
651
681
|
handle.exitCode = code;
|
|
652
682
|
handle.signal = signal;
|
|
@@ -661,7 +691,7 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
661
691
|
const flushThenEmit = () => {
|
|
662
692
|
clearTimeout(emitTimer);
|
|
663
693
|
emitResult();
|
|
664
|
-
resolveExit
|
|
694
|
+
resolveExit();
|
|
665
695
|
};
|
|
666
696
|
const emitTimer = setTimeout(flushThenEmit, 200);
|
|
667
697
|
emitTimer.unref?.();
|
|
@@ -680,35 +710,30 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
680
710
|
}
|
|
681
711
|
// 同 exit:tool_result 先入队,再放行 wait_for_exit。
|
|
682
712
|
emitResult();
|
|
683
|
-
resolveExit
|
|
713
|
+
resolveExit();
|
|
684
714
|
});
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
{
|
|
701
|
-
type: 'tool_result',
|
|
702
|
-
tool_use_id: terminalId,
|
|
703
|
-
content: handle.output,
|
|
704
|
-
is_error: handle.exitCode !== 0,
|
|
705
|
-
},
|
|
706
|
-
],
|
|
707
|
-
},
|
|
708
|
-
timestamp: new Date().toISOString(),
|
|
715
|
+
}
|
|
716
|
+
/** Bash 面板的 tool_result 帧(输出全量 + 是否失败)。 */
|
|
717
|
+
pushTerminalToolResult(terminalId, output, exitCode) {
|
|
718
|
+
this.pushEvents([
|
|
719
|
+
{
|
|
720
|
+
type: 'user',
|
|
721
|
+
message: {
|
|
722
|
+
content: [
|
|
723
|
+
{
|
|
724
|
+
type: 'tool_result',
|
|
725
|
+
tool_use_id: terminalId,
|
|
726
|
+
content: output,
|
|
727
|
+
is_error: exitCode !== 0,
|
|
728
|
+
},
|
|
729
|
+
],
|
|
709
730
|
},
|
|
710
|
-
|
|
711
|
-
|
|
731
|
+
timestamp: new Date().toISOString(),
|
|
732
|
+
},
|
|
733
|
+
]);
|
|
734
|
+
}
|
|
735
|
+
/** Bash 面板的 tool_use 帧(真实命令 + cwd,create 时立刻可见)。 */
|
|
736
|
+
pushTerminalToolUse(terminalId, script, cwd) {
|
|
712
737
|
this.pushEvents([
|
|
713
738
|
{
|
|
714
739
|
type: 'assistant',
|
|
@@ -725,8 +750,6 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
725
750
|
timestamp: new Date().toISOString(),
|
|
726
751
|
},
|
|
727
752
|
]);
|
|
728
|
-
getLogger().info(`[${this.logTag}] terminal created id=${terminalId} command=${params.command} args=${JSON.stringify(params.args ?? [])}`);
|
|
729
|
-
this.currentClient?.respond(id, { terminalId });
|
|
730
753
|
}
|
|
731
754
|
/** terminal/output: 回累计全量输出 + truncated + 当前退出状态(可未退出)。 */
|
|
732
755
|
handleTerminalOutput(id, params) {
|
|
@@ -45,6 +45,12 @@ export declare class OpencodeLogErrorMonitor {
|
|
|
45
45
|
private timer;
|
|
46
46
|
private sessionId;
|
|
47
47
|
private onError;
|
|
48
|
+
/**
|
|
49
|
+
* IO 故障留痕的节流状态:poll 每 2s 一次,同一条 EACCES 不得刷满日志,
|
|
50
|
+
* 但也不能像以前那样完全静默(监视器自己坏了没人知道)。
|
|
51
|
+
*/
|
|
52
|
+
private lastIoError;
|
|
53
|
+
private lastIoErrorAt;
|
|
48
54
|
constructor(opts: OpencodeLogErrorMonitorOptions);
|
|
49
55
|
/**
|
|
50
56
|
* Start monitoring the given session. Replaces any previous monitor state;
|
|
@@ -54,6 +60,11 @@ export declare class OpencodeLogErrorMonitor {
|
|
|
54
60
|
/** Stop monitoring and clear all state. Safe to call multiple times. */
|
|
55
61
|
stop(): void;
|
|
56
62
|
private currentSize;
|
|
63
|
+
/**
|
|
64
|
+
* 监视器自身读不到日志时留痕(同一条最多每分钟一次)。ENOENT 通常是日志
|
|
65
|
+
* 还没生成,属正常空态,不记。
|
|
66
|
+
*/
|
|
67
|
+
private noteIoError;
|
|
57
68
|
private poll;
|
|
58
69
|
/** Match a main-turn stream error line for the monitored session with a
|
|
59
70
|
* limit/quota reason; returns the extracted error text or null. */
|
|
@@ -27,9 +27,12 @@
|
|
|
27
27
|
import { closeSync, openSync, readSync, statSync } from 'node:fs';
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
29
|
import { join } from 'node:path';
|
|
30
|
+
import { getLogger } from '../../../logger/index.js';
|
|
30
31
|
/** Error text we fail fast on: quota/limit conditions opencode backs off on. */
|
|
31
32
|
const LIMIT_ERROR_RE = /usage limit|quota|rate limit|rate_limit|too many requests|insufficient balance|free usage/i;
|
|
32
33
|
const STREAM_ERROR_RE = /message="stream error"/;
|
|
34
|
+
/** 同一条 IO 故障的日志节流窗口。 */
|
|
35
|
+
const IO_ERROR_LOG_INTERVAL_MS = 60_000;
|
|
33
36
|
const MAIN_TURN_RE = /small=false/;
|
|
34
37
|
const ERROR_MESSAGE_RE = /error\.error="((?:[^"\\]|\\.)*)"/;
|
|
35
38
|
/**
|
|
@@ -50,6 +53,12 @@ export class OpencodeLogErrorMonitor {
|
|
|
50
53
|
timer = null;
|
|
51
54
|
sessionId = null;
|
|
52
55
|
onError = null;
|
|
56
|
+
/**
|
|
57
|
+
* IO 故障留痕的节流状态:poll 每 2s 一次,同一条 EACCES 不得刷满日志,
|
|
58
|
+
* 但也不能像以前那样完全静默(监视器自己坏了没人知道)。
|
|
59
|
+
*/
|
|
60
|
+
lastIoError = '';
|
|
61
|
+
lastIoErrorAt = 0;
|
|
53
62
|
constructor(opts) {
|
|
54
63
|
this.logPath = opts.logPath;
|
|
55
64
|
this.pollIntervalMs = opts.pollIntervalMs ?? 2000;
|
|
@@ -81,10 +90,28 @@ export class OpencodeLogErrorMonitor {
|
|
|
81
90
|
try {
|
|
82
91
|
return statSync(this.logPath).size;
|
|
83
92
|
}
|
|
84
|
-
catch {
|
|
93
|
+
catch (err) {
|
|
94
|
+
this.noteIoError(err);
|
|
85
95
|
return 0;
|
|
86
96
|
}
|
|
87
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* 监视器自身读不到日志时留痕(同一条最多每分钟一次)。ENOENT 通常是日志
|
|
100
|
+
* 还没生成,属正常空态,不记。
|
|
101
|
+
*/
|
|
102
|
+
noteIoError(err) {
|
|
103
|
+
if (err.code === 'ENOENT')
|
|
104
|
+
return;
|
|
105
|
+
const message = err?.message ?? String(err);
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
if (message === this.lastIoError && now - this.lastIoErrorAt < IO_ERROR_LOG_INTERVAL_MS) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.lastIoError = message;
|
|
111
|
+
this.lastIoErrorAt = now;
|
|
112
|
+
getLogger().warn(`[opencode-error-monitor] cannot read ${this.logPath}: ${message}; ` +
|
|
113
|
+
'limit errors will fall back to the idle watchdog');
|
|
114
|
+
}
|
|
88
115
|
poll() {
|
|
89
116
|
if (this.sessionId === null || this.onError === null)
|
|
90
117
|
return;
|
|
@@ -107,9 +134,11 @@ export class OpencodeLogErrorMonitor {
|
|
|
107
134
|
closeSync(fd);
|
|
108
135
|
}
|
|
109
136
|
}
|
|
110
|
-
catch {
|
|
137
|
+
catch (err) {
|
|
138
|
+
this.noteIoError(err);
|
|
111
139
|
return;
|
|
112
140
|
}
|
|
141
|
+
this.lastIoError = '';
|
|
113
142
|
this.offset = size;
|
|
114
143
|
this.pendingTail += chunk;
|
|
115
144
|
}
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
* - OpencodeLogErrorMonitor tails opencode's own log for LLM stream errors
|
|
20
20
|
* the ACP protocol never forwards
|
|
21
21
|
*/
|
|
22
|
-
import type { AgentKind
|
|
22
|
+
import type { AgentKind } from '../../../agent-kinds.js';
|
|
23
|
+
import type { AgentSessionReader } from '../../../session-contract.js';
|
|
24
|
+
import type { AgentStatusInfo } from '../../types.js';
|
|
23
25
|
import { JsonRpcClient } from '../../common/jsonrpc/client.js';
|
|
24
26
|
import { OpencodeAcpTranslator } from './translator.js';
|
|
25
27
|
import { type SessionNewResult, type SessionResumeResult, type SessionPromptResult } from '../../common/acp/protocol-types.js';
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* - pi,极简风格,没有 Ask User question。
|
|
18
18
|
* - `prompt` ack ≠ turn completion; the turn ends on `agent_settled`.
|
|
19
19
|
*/
|
|
20
|
-
import type { AgentSessionReader
|
|
20
|
+
import type { AgentSessionReader } from '../../../session-contract.js';
|
|
21
|
+
import type { AgentEvent, AgentStatusInfo, SpawnOptions } from '../../types.js';
|
|
21
22
|
import { ConnectionBasedRunner } from '../../common/connection-based-runner.js';
|
|
22
23
|
import { PiRpcClient } from './client.js';
|
|
23
24
|
import { type PiRpcTranslatorEvent } from './translator.js';
|
package/dist/runner/probe.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* does PATH + X_OK on posix (same semantics) and PATH × PATHEXT on win32,
|
|
18
18
|
* with no child process at all.
|
|
19
19
|
*/
|
|
20
|
-
import type { AgentKind } from '
|
|
20
|
+
import type { AgentKind } from '../agent-kinds.js';
|
|
21
21
|
/**
|
|
22
22
|
* Probe a single agent, using cache when fresh.
|
|
23
23
|
* Updates cache on completion.
|