lark-remote 0.5.19 → 0.5.21
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 +29 -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 +72 -46
- package/dist/bridge/index.d.ts +106 -153
- package/dist/bridge/index.js +400 -1000
- 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 +141 -130
- package/dist/card/card-budget.js +123 -98
- 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 +114 -159
- package/dist/card/run-state.d.ts +1 -15
- package/dist/card/run-state.js +358 -292
- package/dist/card/tool-render.d.ts +1 -1
- package/dist/card/tool-render.js +21 -17
- package/dist/card/usage-format.d.ts +22 -7
- package/dist/card/usage-format.js +119 -100
- package/dist/cli.js +15 -3
- package/dist/clone.js +2 -2
- package/dist/common/text-width.js +36 -24
- package/dist/common/tool-summary.js +132 -90
- 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 +387 -281
- package/dist/logger/index.js +6 -6
- package/dist/message-handlers.d.ts +0 -5
- package/dist/message-handlers.js +254 -237
- 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/ls.d.ts +3 -0
- package/dist/router/commands/ls.js +256 -175
- package/dist/router/commands/order.js +184 -159
- package/dist/router/commands/resume.js +190 -152
- package/dist/router/commands/ws.js +219 -174
- 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 +401 -391
- package/dist/runner/bash/runner.js +2 -4
- 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 +2 -5
- package/dist/runner/codex/app-server/translator.js +291 -238
- 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 +50 -26
- 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/jsonl-stream.js +154 -187
- package/dist/runner/common/jsonrpc/client.d.ts +29 -0
- package/dist/runner/common/jsonrpc/client.js +43 -12
- 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 +6 -1
- package/dist/runner/common/spawning-runner.js +9 -4
- package/dist/runner/common/turn-started.d.ts +8 -0
- package/dist/runner/common/turn-started.js +11 -0
- package/dist/runner/dsh/approval.js +84 -51
- 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 +7 -11
- package/dist/runner/dsh/client.js +356 -289
- 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/mux-stream.js +28 -8
- package/dist/runner/dsh/runner.d.ts +44 -4
- package/dist/runner/dsh/runner.js +296 -199
- package/dist/runner/dsh/translator.d.ts +1 -31
- package/dist/runner/dsh/translator.js +180 -178
- 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 +82 -46
- package/dist/runner/kimi/acp/translator.d.ts +1 -1
- package/dist/runner/kimi/acp/translator.js +3 -4
- package/dist/runner/opencode/acp/error-monitor.js +5 -5
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +4 -4
- package/dist/runner/pi/rpc/client.js +4 -13
- 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/pi/rpc/translator.js +61 -60
- 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 +160 -122
- package/dist/session/codex/rollout-reader.js +181 -182
- 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 +291 -235
- package/dist/session/kimi/sessions.d.ts +40 -8
- package/dist/session/kimi/sessions.js +373 -301
- package/dist/session/opencode/sessions.d.ts +142 -1
- package/dist/session/opencode/sessions.js +378 -321
- package/dist/session/pi/sessions.js +4 -5
- package/dist/session/session-store.d.ts +9 -1
- package/dist/session/session-store.js +99 -99
- 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
|
@@ -133,8 +133,8 @@ export class OpencodeAcpRunner extends BaseAcpRunner {
|
|
|
133
133
|
}
|
|
134
134
|
// Apply the configured model (`provider/model`) via set_config_option.
|
|
135
135
|
// Without this the session runs opencode's own default model (e.g.
|
|
136
|
-
// opencode/big-pickle), which may be unusable
|
|
137
|
-
//
|
|
136
|
+
// opencode/big-pickle), which may be unusable: the default zen model can
|
|
137
|
+
// leave session/prompt hanging with no response.
|
|
138
138
|
// Non-fatal on failure (unknown model → server InvalidModelError).
|
|
139
139
|
if (this.model && this.currentModelValue !== this.model) {
|
|
140
140
|
try {
|
|
@@ -179,7 +179,7 @@ export class OpencodeAcpRunner extends BaseAcpRunner {
|
|
|
179
179
|
* before failing the turn.
|
|
180
180
|
*
|
|
181
181
|
* 单次 attempt 自身还要等 opencode 侧超时(~10s),所以真实窗口 ≈ Σdelay +
|
|
182
|
-
* 每次的等待。
|
|
182
|
+
* 每次的等待。daemon 刚重启时 `/api/model` 会因模型目录拉取
|
|
183
183
|
* 超时连续 503 约 7 分钟,旧退避 0.5/1.5/3s(合计 ~45s)全落在这段里,4 次
|
|
184
184
|
* 重试无一成功。现在铺到 ~75s + 每次超时 ≈ 2 分钟,覆盖一次冷启动窗口,
|
|
185
185
|
* 又不至于让真故障的用户干等太久。
|
|
@@ -194,7 +194,7 @@ export class OpencodeAcpRunner extends BaseAcpRunner {
|
|
|
194
194
|
/**
|
|
195
195
|
* 用户可见文案。这里刻意**不**建议 `opencode service restart`:卡住的是
|
|
196
196
|
* opencode 的模型目录/位置服务冷启动,重启只会清掉刚热好的状态、重新回到
|
|
197
|
-
*
|
|
197
|
+
* 同一个窗口(照提示重启两次仍然失败,等几分钟自己好了)。
|
|
198
198
|
*/
|
|
199
199
|
enrichSessionSetupError(err) {
|
|
200
200
|
if (!this.isRetryableSessionSetupError(err)) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Response correlation is by command id; every non-response line is forwarded
|
|
11
11
|
* as an event to the active hooks (the runner's translator).
|
|
12
12
|
*/
|
|
13
|
-
import { ConnectionLostError } from '../../common/jsonrpc/client.js';
|
|
13
|
+
import { ConnectionLostError, disposeRpcClientInstance, rejectAllPending, } from '../../common/jsonrpc/client.js';
|
|
14
14
|
export class PiRpcClient {
|
|
15
15
|
transport;
|
|
16
16
|
requestTimeoutMs;
|
|
@@ -78,13 +78,8 @@ export class PiRpcClient {
|
|
|
78
78
|
return;
|
|
79
79
|
this.transport.write({ ...command, id: `req_${this.nextId++}` });
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return;
|
|
84
|
-
this._disposed = true;
|
|
85
|
-
this._ready = false;
|
|
86
|
-
this.failPending(new ConnectionLostError('pi rpc client disposed'));
|
|
87
|
-
await this.transport.close();
|
|
81
|
+
dispose() {
|
|
82
|
+
return disposeRpcClientInstance(this, this.pending, () => this.transport.close(), 'pi rpc client disposed');
|
|
88
83
|
}
|
|
89
84
|
// =========================================================================
|
|
90
85
|
// Internal
|
|
@@ -104,11 +99,7 @@ export class PiRpcClient {
|
|
|
104
99
|
this.runHooks?.onEvent?.(message);
|
|
105
100
|
}
|
|
106
101
|
failPending(err) {
|
|
107
|
-
|
|
108
|
-
clearTimeout(pending.timer);
|
|
109
|
-
pending.reject(err);
|
|
110
|
-
}
|
|
111
|
-
this.pending.clear();
|
|
102
|
+
rejectAllPending(this.pending, err);
|
|
112
103
|
}
|
|
113
104
|
}
|
|
114
105
|
//# sourceMappingURL=client.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and events (session lifecycle / message / compaction) as bare
|
|
8
8
|
* JSON objects (no envelope).
|
|
9
9
|
*
|
|
10
|
-
* Turn semantics (verified
|
|
10
|
+
* Turn semantics (verified against pi 0.84.1):
|
|
11
11
|
* - `prompt` returns an ack (`success:true`) immediately; the turn then streams
|
|
12
12
|
* events and completes with an `agent_settled` event.
|
|
13
13
|
* - `compact` runs synchronously and returns its response at completion; it also
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and events (session lifecycle / message / compaction) as bare
|
|
8
8
|
* JSON objects (no envelope).
|
|
9
9
|
*
|
|
10
|
-
* Turn semantics (verified
|
|
10
|
+
* Turn semantics (verified against pi 0.84.1):
|
|
11
11
|
* - `prompt` returns an ack (`success:true`) immediately; the turn then streams
|
|
12
12
|
* events and completes with an `agent_settled` event.
|
|
13
13
|
* - `compact` runs synchronously and returns its response at completion; it also
|
|
@@ -59,6 +59,7 @@ export declare class PiRpcRunner extends ConnectionBasedRunner<PiRpcClient, PiRp
|
|
|
59
59
|
protected clearTurnState(): void;
|
|
60
60
|
protected releaseConnection(cwd: string): Promise<void>;
|
|
61
61
|
protected notifyIdle(cwd: string): void;
|
|
62
|
+
protected notifyConnectionActivity(cwd: string): void;
|
|
62
63
|
protected disposeConnections(): Promise<void>;
|
|
63
64
|
getStatusInfo(): AgentStatusInfo;
|
|
64
65
|
/**
|
|
@@ -72,4 +73,6 @@ export declare class PiRpcRunner extends ConnectionBasedRunner<PiRpcClient, PiRp
|
|
|
72
73
|
*/
|
|
73
74
|
runCompact(_message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
|
|
74
75
|
private handleEvent;
|
|
76
|
+
/** Wire the acquired client's hooks into the turn dispatcher (anchor-guarded). */
|
|
77
|
+
private installTurnHooks;
|
|
75
78
|
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { ConnectionBasedRunner } from '../../common/connection-based-runner.js';
|
|
21
21
|
import { ConnectionManager } from '../../common/jsonrpc/connection-manager.js';
|
|
22
22
|
import { getLogger } from '../../../logger/index.js';
|
|
23
|
+
import { PI_DEFAULTS } from '../../../config/index.js';
|
|
23
24
|
import { PiRpcClient } from './client.js';
|
|
24
25
|
import { PiRpcTranslator } from './translator.js';
|
|
25
26
|
export class PiRpcRunner extends ConnectionBasedRunner {
|
|
@@ -36,10 +37,12 @@ export class PiRpcRunner extends ConnectionBasedRunner {
|
|
|
36
37
|
sessionReader: opts.sessionReader,
|
|
37
38
|
turnIdleTimeoutMs: opts.turnIdleTimeoutMs,
|
|
38
39
|
});
|
|
39
|
-
this.defaultModel = opts.model ??
|
|
40
|
-
this.provider = opts.provider ??
|
|
41
|
-
this.thinking = opts.thinking ??
|
|
42
|
-
|
|
40
|
+
this.defaultModel = opts.model ?? PI_DEFAULTS.model;
|
|
41
|
+
this.provider = opts.provider ?? PI_DEFAULTS.provider;
|
|
42
|
+
this.thinking = opts.thinking ?? PI_DEFAULTS.thinking;
|
|
43
|
+
// PI_DEFAULTS.tools 是逗号串(config schema 的 tools 同形态),与原数组兜底
|
|
44
|
+
// join(',') 结果逐字相等;opts.tools 自身的数组语义(含空数组 → 空串)不变。
|
|
45
|
+
const tools = opts.tools?.join(',') ?? PI_DEFAULTS.tools;
|
|
43
46
|
this.manager = new ConnectionManager({
|
|
44
47
|
binary: opts.binary ?? 'pi',
|
|
45
48
|
buildArgs: (req) => buildPiArgs(this.provider, this.defaultModel, this.thinking, tools, req.sessionId),
|
|
@@ -93,6 +96,9 @@ export class PiRpcRunner extends ConnectionBasedRunner {
|
|
|
93
96
|
notifyIdle(cwd) {
|
|
94
97
|
this.manager.notifyIdle(cwd);
|
|
95
98
|
}
|
|
99
|
+
notifyConnectionActivity(cwd) {
|
|
100
|
+
this.manager.notifyActivity(cwd);
|
|
101
|
+
}
|
|
96
102
|
async disposeConnections() {
|
|
97
103
|
await this.manager.disposeAll();
|
|
98
104
|
}
|
|
@@ -110,28 +116,13 @@ export class PiRpcRunner extends ConnectionBasedRunner {
|
|
|
110
116
|
* `agent_settled` event produces the result.
|
|
111
117
|
*/
|
|
112
118
|
async setupTurn(message, opts) {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// 旧轮迟到的 agent_settled 同理(新 turn 空转立即"成功")。所有 hook
|
|
121
|
-
// 落地前必须过 isCurrentTurn。
|
|
122
|
-
const generation = this.turnGeneration;
|
|
123
|
-
client.setHooks({
|
|
124
|
-
onEvent: (evt) => {
|
|
125
|
-
if (!this.isCurrentTurn(generation, client))
|
|
126
|
-
return;
|
|
127
|
-
this.handleEvent(evt);
|
|
128
|
-
},
|
|
129
|
-
onClose: () => {
|
|
130
|
-
if (!this.isCurrentTurn(generation, client))
|
|
131
|
-
return;
|
|
132
|
-
this.failTurn('Pi RPC connection closed');
|
|
133
|
-
},
|
|
134
|
-
});
|
|
119
|
+
const anchor = await this.acquireForTurn(opts.cwd, () => this.manager.acquire(opts.cwd, { sessionId: opts.sessionId }));
|
|
120
|
+
// pi 的 shouldReuse 要求 sessionId 匹配:/new、/resume 切换后的首条消息会让
|
|
121
|
+
// acquire **同步 dispose** 旧连接。旧 client 残留的上一轮 onClose / 迟到的
|
|
122
|
+
// agent_settled 必须被 anchor 守卫挡掉,否则假错误会推进新 turn 的共享队列
|
|
123
|
+
// (语义见 acquireForTurn / TurnAnchor)。
|
|
124
|
+
this.installTurnHooks(anchor);
|
|
125
|
+
const { client } = anchor;
|
|
135
126
|
let sessionId = opts.sessionId;
|
|
136
127
|
if (!sessionId) {
|
|
137
128
|
const state = await client.request({ type: 'get_state' });
|
|
@@ -169,24 +160,11 @@ export class PiRpcRunner extends ConnectionBasedRunner {
|
|
|
169
160
|
if (!opts.sessionId) {
|
|
170
161
|
throw new Error('compact requires a sessionId');
|
|
171
162
|
}
|
|
172
|
-
const client = await this.manager.acquire(opts.cwd, { sessionId: opts.sessionId });
|
|
173
|
-
this.manager.notifyActivity(opts.cwd);
|
|
174
|
-
this.currentClient = client;
|
|
175
163
|
// 同 setupTurn:hook 带轮次代际锚点,旧连接的迟到回调不得进入本轮。
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
onEvent: (evt) => {
|
|
179
|
-
if (!this.isCurrentTurn(generation, client))
|
|
180
|
-
return;
|
|
181
|
-
this.handleEvent(evt);
|
|
182
|
-
},
|
|
183
|
-
onClose: () => {
|
|
184
|
-
if (!this.isCurrentTurn(generation, client))
|
|
185
|
-
return;
|
|
186
|
-
this.failTurn('Pi RPC connection closed');
|
|
187
|
-
},
|
|
188
|
-
});
|
|
164
|
+
const anchor = await this.acquireForTurn(opts.cwd, () => this.manager.acquire(opts.cwd, { sessionId: opts.sessionId }));
|
|
165
|
+
this.installTurnHooks(anchor);
|
|
189
166
|
this.activeSessionId = opts.sessionId;
|
|
167
|
+
const { client } = anchor;
|
|
190
168
|
const translator = new PiRpcTranslator();
|
|
191
169
|
translator.setSessionId(opts.sessionId);
|
|
192
170
|
translator.setOperationKind('compact');
|
|
@@ -210,6 +188,13 @@ export class PiRpcRunner extends ConnectionBasedRunner {
|
|
|
210
188
|
handleEvent(evt) {
|
|
211
189
|
this.pushEvents(this.currentTranslator?.handleEvent(evt) ?? []);
|
|
212
190
|
}
|
|
191
|
+
/** Wire the acquired client's hooks into the turn dispatcher (anchor-guarded). */
|
|
192
|
+
installTurnHooks(anchor) {
|
|
193
|
+
anchor.client.setHooks({
|
|
194
|
+
onEvent: this.forCurrentTurn(anchor, 'event', (evt) => this.handleEvent(evt)),
|
|
195
|
+
onClose: this.forCurrentTurn(anchor, 'connection close', () => this.failTurn('Pi RPC connection closed')),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
213
198
|
}
|
|
214
199
|
/** Build pi `--mode rpc` spawn args. `sessionId` is bound at spawn (pi has no
|
|
215
200
|
* RPC resume-by-id command); omitting it spawns a brand-new session. */
|
|
@@ -10,7 +10,56 @@
|
|
|
10
10
|
* `result` from that response.
|
|
11
11
|
* - `agent_settled` is ignored for compaction turns (no double result).
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { produceTurnStartedEvent } from '../../common/turn-started.js';
|
|
14
|
+
/** 首见:push 一个空 text/thinking 块并把游标移到它。 */
|
|
15
|
+
function textLikeStart(kind, state, _evt) {
|
|
16
|
+
state.content.push(kind === 'text' ? { type: 'text', text: '' } : { type: 'thinking', thinking: '' });
|
|
17
|
+
state.currentContentIndex++;
|
|
18
|
+
}
|
|
19
|
+
/** 增量:把 delta 追加到当前 text/thinking 块(类型不匹配则忽略)。 */
|
|
20
|
+
function textLikeDelta(kind, state, evt) {
|
|
21
|
+
const idx = state.currentContentIndex;
|
|
22
|
+
const block = state.content[idx];
|
|
23
|
+
if (idx >= 0 && block?.type === kind) {
|
|
24
|
+
block[kind] += evt.delta ?? '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** 收尾:以全量 content 覆盖当前 text/thinking 块(无 content 则忽略)。 */
|
|
28
|
+
function textLikeEnd(kind, state, evt) {
|
|
29
|
+
const idx = state.currentContentIndex;
|
|
30
|
+
const block = state.content[idx];
|
|
31
|
+
if (idx >= 0 && block?.type === kind && evt.content) {
|
|
32
|
+
block[kind] = evt.content;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function toolCallStart(state, _evt) {
|
|
36
|
+
// 信息保真 不再 push 空壳 tool_use(空 id/name 会被渲染成
|
|
37
|
+
// 无意义工具块)。只保留 bookkeeping,toolcall_end 补发完整事件。
|
|
38
|
+
state.pendingToolCall = true;
|
|
39
|
+
}
|
|
40
|
+
function toolCallEnd(state, evt) {
|
|
41
|
+
if (state.pendingToolCall && evt.toolCall) {
|
|
42
|
+
state.content.push({
|
|
43
|
+
type: 'tool_use',
|
|
44
|
+
id: evt.toolCall.id,
|
|
45
|
+
name: evt.toolCall.name,
|
|
46
|
+
input: evt.toolCall.arguments,
|
|
47
|
+
});
|
|
48
|
+
state.currentContentIndex = state.content.length - 1;
|
|
49
|
+
state.pendingToolCall = false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** 查表分派 message_update;toolcall_delta 等无专属 handler 的类型为 no-op。 */
|
|
53
|
+
const PI_UPDATE_HANDLERS = new Map([
|
|
54
|
+
['text_start', textLikeStart.bind(null, 'text')],
|
|
55
|
+
['text_delta', textLikeDelta.bind(null, 'text')],
|
|
56
|
+
['text_end', textLikeEnd.bind(null, 'text')],
|
|
57
|
+
['thinking_start', textLikeStart.bind(null, 'thinking')],
|
|
58
|
+
['thinking_delta', textLikeDelta.bind(null, 'thinking')],
|
|
59
|
+
['thinking_end', textLikeEnd.bind(null, 'thinking')],
|
|
60
|
+
['toolcall_start', toolCallStart],
|
|
61
|
+
['toolcall_end', toolCallEnd],
|
|
62
|
+
]);
|
|
14
63
|
export class PiRpcTranslator {
|
|
15
64
|
content = [];
|
|
16
65
|
currentContentIndex = -1;
|
|
@@ -135,8 +184,9 @@ export class PiRpcTranslator {
|
|
|
135
184
|
}
|
|
136
185
|
/** Self-produced turn_started (no wire equivalent). */
|
|
137
186
|
produceTurnStarted(sessionId, turnId) {
|
|
138
|
-
this.
|
|
139
|
-
|
|
187
|
+
return produceTurnStartedEvent(sessionId, turnId, this.operationKind, (s) => {
|
|
188
|
+
this.sessionId = s;
|
|
189
|
+
});
|
|
140
190
|
}
|
|
141
191
|
/** Result for a completed normal turn (agent_settled). */
|
|
142
192
|
produceResultFromSettled() {
|
|
@@ -236,63 +286,14 @@ export class PiRpcTranslator {
|
|
|
236
286
|
this.lastTotalTokens = u.totalTokens;
|
|
237
287
|
}
|
|
238
288
|
applyUpdate(evt) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.content[this.currentContentIndex].text += evt.delta ?? '';
|
|
248
|
-
}
|
|
249
|
-
break;
|
|
250
|
-
case 'text_end':
|
|
251
|
-
if (this.currentContentIndex >= 0 &&
|
|
252
|
-
this.content[this.currentContentIndex]?.type === 'text' &&
|
|
253
|
-
evt.content) {
|
|
254
|
-
this.content[this.currentContentIndex].text = evt.content;
|
|
255
|
-
}
|
|
256
|
-
break;
|
|
257
|
-
case 'thinking_start':
|
|
258
|
-
this.content.push({ type: 'thinking', thinking: '' });
|
|
259
|
-
this.currentContentIndex++;
|
|
260
|
-
break;
|
|
261
|
-
case 'thinking_delta':
|
|
262
|
-
if (this.currentContentIndex >= 0 &&
|
|
263
|
-
this.content[this.currentContentIndex]?.type === 'thinking') {
|
|
264
|
-
this.content[this.currentContentIndex].thinking +=
|
|
265
|
-
evt.delta ?? '';
|
|
266
|
-
}
|
|
267
|
-
break;
|
|
268
|
-
case 'thinking_end':
|
|
269
|
-
if (this.currentContentIndex >= 0 &&
|
|
270
|
-
this.content[this.currentContentIndex]?.type === 'thinking' &&
|
|
271
|
-
evt.content) {
|
|
272
|
-
this.content[this.currentContentIndex].thinking = evt.content;
|
|
273
|
-
}
|
|
274
|
-
break;
|
|
275
|
-
case 'toolcall_start':
|
|
276
|
-
// 信息保真 不再 push 空壳 tool_use(空 id/name 会被渲染成
|
|
277
|
-
// 无意义工具块)。只保留 bookkeeping,toolcall_end 补发完整事件。
|
|
278
|
-
this.pendingToolCall = true;
|
|
279
|
-
break;
|
|
280
|
-
case 'toolcall_end':
|
|
281
|
-
if (this.pendingToolCall && evt.toolCall) {
|
|
282
|
-
this.content.push({
|
|
283
|
-
type: 'tool_use',
|
|
284
|
-
id: evt.toolCall.id,
|
|
285
|
-
name: evt.toolCall.name,
|
|
286
|
-
input: evt.toolCall.arguments,
|
|
287
|
-
});
|
|
288
|
-
this.currentContentIndex = this.content.length - 1;
|
|
289
|
-
this.pendingToolCall = false;
|
|
290
|
-
}
|
|
291
|
-
break;
|
|
292
|
-
default:
|
|
293
|
-
// toolcall_delta: pi provides complete arguments at toolcall_end, ignore.
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
289
|
+
const state = {
|
|
290
|
+
content: this.content,
|
|
291
|
+
currentContentIndex: this.currentContentIndex,
|
|
292
|
+
pendingToolCall: this.pendingToolCall,
|
|
293
|
+
};
|
|
294
|
+
PI_UPDATE_HANDLERS.get(evt.type)?.(state, evt);
|
|
295
|
+
this.currentContentIndex = state.currentContentIndex;
|
|
296
|
+
this.pendingToolCall = state.pendingToolCall;
|
|
296
297
|
}
|
|
297
298
|
}
|
|
298
299
|
/**
|
package/dist/runner/types.d.ts
CHANGED
|
@@ -302,7 +302,7 @@ export interface ResultEvent {
|
|
|
302
302
|
* 省略)` 标记——渲染层再怎么「回答优先」也找不回被丢掉的头部。runner 在
|
|
303
303
|
* 收 turn 时拿到的是**完整** assistant 文本(一条消息,不是逐 delta 拼接),
|
|
304
304
|
* 把它挂在 result 上,`promoteFinalAnswer` 用它建立 finalAnswer,回答因此
|
|
305
|
-
*
|
|
305
|
+
* 不再被进度窗口截断。
|
|
306
306
|
*/
|
|
307
307
|
finalAnswerText?: string;
|
|
308
308
|
usage?: {
|
|
@@ -582,7 +582,7 @@ export { AGENT_DISPLAY_NAMES } from '../agent-kinds.js';
|
|
|
582
582
|
* 键集就是 `AgentKind`:新增 agent 时缺一项 = TypeScript 报错,必须显式回答
|
|
583
583
|
* 「这个 agent 能不能压缩」。历史教训:能力只由 runner 上有没有 `runCompact`
|
|
584
584
|
* 这个鸭子方法隐式决定,dsh 接入时漏了实现,run 卡片直接不渲染按钮——没有
|
|
585
|
-
*
|
|
585
|
+
* 报错、没有置灰、没有日志,用户只能看到一个缺失的按钮。
|
|
586
586
|
*
|
|
587
587
|
* 声明与实现的唯一性由 `src/runner/runner-structure.test.ts` 的矩阵用例保证:
|
|
588
588
|
* 每个 kind 的声明值必须等于对应 runner 类是否实现 `runCompact`。bridge 侧
|
package/dist/runner/types.js
CHANGED
|
@@ -11,7 +11,7 @@ export { AGENT_DISPLAY_NAMES } from '../agent-kinds.js';
|
|
|
11
11
|
* 键集就是 `AgentKind`:新增 agent 时缺一项 = TypeScript 报错,必须显式回答
|
|
12
12
|
* 「这个 agent 能不能压缩」。历史教训:能力只由 runner 上有没有 `runCompact`
|
|
13
13
|
* 这个鸭子方法隐式决定,dsh 接入时漏了实现,run 卡片直接不渲染按钮——没有
|
|
14
|
-
*
|
|
14
|
+
* 报错、没有置灰、没有日志,用户只能看到一个缺失的按钮。
|
|
15
15
|
*
|
|
16
16
|
* 声明与实现的唯一性由 `src/runner/runner-structure.test.ts` 的矩阵用例保证:
|
|
17
17
|
* 每个 kind 的声明值必须等于对应 runner 类是否实现 `runCompact`。bridge 侧
|
|
@@ -25,6 +25,50 @@ export declare function getNewestSession(cwd: string, opts?: {
|
|
|
25
25
|
export declare function isClaudeSessionActive(sessionId: string, cwd: string, opts?: {
|
|
26
26
|
projectsDir?: string;
|
|
27
27
|
}): boolean;
|
|
28
|
+
/** claude wire 一条 assistant 行携带的原始 usage 字段(可选,缺按 0 计)。 */
|
|
29
|
+
interface ClaudeRawUsage {
|
|
30
|
+
input_tokens?: number;
|
|
31
|
+
output_tokens?: number;
|
|
32
|
+
cache_read_input_tokens?: number;
|
|
33
|
+
cache_creation_input_tokens?: number;
|
|
34
|
+
}
|
|
35
|
+
/** per-message-id 合并后的 usage 采样(四个分量 + 采集时的真人 turn 序号)。 */
|
|
36
|
+
export interface ClaudePerMessageUsage {
|
|
37
|
+
input: number;
|
|
38
|
+
output: number;
|
|
39
|
+
cacheRead: number;
|
|
40
|
+
cacheCreation: number;
|
|
41
|
+
/** 该 message 属于第几个真人 turn(0 = 还没有真人 prompt)。 */
|
|
42
|
+
turn: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 两条采样逐字段取 max(纯函数;UsageAccumulator 的 per-field max 语义)。
|
|
46
|
+
*
|
|
47
|
+
* RED LINE(token 口径单源的一部分):一条 streaming assistant 消息跨多行
|
|
48
|
+
* jsonl、共享同一 message id;部分后端(第三方网关,如 DeepSeek)在早期占位
|
|
49
|
+
* 行(空 thinking 块)写全零 usage,真实用量只在带 stop_reason 的末行。
|
|
50
|
+
* first-occurrence-wins 会把零记录钉死、丢掉真实用量(regression 2026-08-19:
|
|
51
|
+
* auto-resume 卡 per-run 统计全 0)。逐字段 max 与行序无关地重建完整用量;
|
|
52
|
+
* 官方 API 每行重复相同 usage,max 是 no-op。
|
|
53
|
+
*
|
|
54
|
+
* 产生:合并后的一条采样。依赖:入参。不改任何外部状态。
|
|
55
|
+
*/
|
|
56
|
+
export declare function maxUsageRecord(prev: ClaudePerMessageUsage | undefined, usage: ClaudeRawUsage, turnSeq: number): ClaudePerMessageUsage;
|
|
57
|
+
/**
|
|
58
|
+
* usage 装配(纯函数):每个 message id 一条最优记录、按首次出现顺序喂
|
|
59
|
+
* UsageAccumulator(Map 保序),acc.last = 末条 message 的完整用量,totals
|
|
60
|
+
* 让每条 API 响应恰好多计一次。per-run flow 只取末个真人 turn 的记录——
|
|
61
|
+
* 工具往返的 turn 有多条 assistant 消息(一次模型请求一条),只取 `last`
|
|
62
|
+
* 会低报。
|
|
63
|
+
*
|
|
64
|
+
* 产生:AgentSessionUsage。依赖:入参 records + 标量。不改任何外部状态
|
|
65
|
+
* (UsageAccumulator 在函数内新建)。
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildClaudeUsage(records: Iterable<ClaudePerMessageUsage>, opts: {
|
|
68
|
+
turnSeq: number;
|
|
69
|
+
lastPostTokens: number;
|
|
70
|
+
compactCount: number;
|
|
71
|
+
}): AgentSessionUsage;
|
|
28
72
|
export declare function readSessionContent(sessionId: string, cwd: string, opts?: {
|
|
29
73
|
projectsDir?: string;
|
|
30
74
|
maxEvents?: number;
|
|
@@ -35,3 +79,4 @@ export declare function readSessionContent(sessionId: string, cwd: string, opts?
|
|
|
35
79
|
recap?: string;
|
|
36
80
|
displayTitle?: string;
|
|
37
81
|
};
|
|
82
|
+
export {};
|