lark-remote 0.5.4 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/bridge/approval-coordinator.d.ts +18 -1
- package/dist/bridge/approval-coordinator.js +57 -10
- package/dist/bridge/index.js +90 -17
- package/dist/card/approval-render.js +18 -0
- package/dist/card/run-state.js +10 -5
- package/dist/config/index.d.ts +16 -0
- package/dist/config/index.js +46 -6
- package/dist/config/opencode-config.d.ts +3 -3
- package/dist/config/opencode-config.js +38 -22
- package/dist/inbound/authorize.d.ts +34 -0
- package/dist/inbound/authorize.js +31 -0
- package/dist/index.js +116 -63
- package/dist/instance-lock.d.ts +9 -0
- package/dist/instance-lock.js +113 -42
- package/dist/router/config/dsh.d.ts +51 -21
- package/dist/router/config/dsh.js +255 -66
- package/dist/router/config/opencode.js +16 -6
- package/dist/router/config/types.d.ts +6 -3
- package/dist/router/index.d.ts +1 -0
- package/dist/router/index.js +13 -5
- package/dist/runner/common/acp/base-acp-runner.d.ts +12 -0
- package/dist/runner/common/acp/base-acp-runner.js +83 -8
- package/dist/runner/common/acp/protocol-types.d.ts +10 -2
- package/dist/runner/common/acp/protocol-types.js +3 -1
- package/dist/runner/common/connection-based-runner.d.ts +15 -0
- package/dist/runner/common/connection-based-runner.js +40 -5
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +15 -0
- package/dist/runner/common/jsonrpc/connection-manager.js +83 -50
- package/dist/runner/dsh/approval.d.ts +53 -0
- package/dist/runner/dsh/approval.js +161 -0
- package/dist/runner/dsh/auth.d.ts +36 -0
- package/dist/runner/dsh/auth.js +118 -0
- package/dist/runner/dsh/client.d.ts +161 -42
- package/dist/runner/dsh/client.js +551 -244
- package/dist/runner/dsh/daemon.d.ts +85 -0
- package/dist/runner/dsh/daemon.js +272 -0
- package/dist/runner/dsh/merge.d.ts +9 -0
- package/dist/runner/dsh/merge.js +51 -0
- package/dist/runner/dsh/runner.d.ts +94 -11
- package/dist/runner/dsh/runner.js +553 -67
- package/dist/runner/dsh/translator.d.ts +43 -3
- package/dist/runner/dsh/translator.js +115 -38
- package/dist/runner/dsh/types.d.ts +189 -27
- package/dist/runner/dsh/types.js +11 -5
- package/dist/runner/kimi/acp/runner.js +15 -3
- package/dist/runner/opencode/acp/error-monitor.d.ts +2 -2
- package/dist/runner/opencode/acp/error-monitor.js +2 -2
- package/dist/runner/opencode/acp/runner.d.ts +9 -0
- package/dist/runner/opencode/acp/runner.js +40 -1
- package/dist/runner/opencode/acp/translator.d.ts +4 -2
- package/dist/runner/opencode/acp/translator.js +12 -3
- package/dist/runner/pi/rpc/translator.d.ts +2 -0
- package/dist/runner/pi/rpc/translator.js +27 -1
- package/dist/runner/types.d.ts +34 -0
- package/dist/runner/types.js +24 -0
- package/dist/session/dsh/sessions.d.ts +45 -6
- package/dist/session/dsh/sessions.js +171 -27
- package/dist/session/opencode/sessions.d.ts +18 -7
- package/dist/session/opencode/sessions.js +294 -15
- package/dist/session/pi/sessions.js +6 -2
- package/package.json +3 -1
|
@@ -12,17 +12,57 @@
|
|
|
12
12
|
* completed → success; aborted/interrupted → interrupted;
|
|
13
13
|
* blocked/error/max-tokens → error (errorMessage carries the reason)
|
|
14
14
|
*
|
|
15
|
+
* Token accounting (ccusage 口径, AGENTS.md §9.21): DSH emits ONE usage per model
|
|
16
|
+
* call (per step) — a tool-looping turn has several `assistant/message` events.
|
|
17
|
+
* The result event therefore carries **the sum of the turn's samplings**, not the
|
|
18
|
+
* last one (codex `token_count` has the same shape). `lastUsage` is kept only as
|
|
19
|
+
* the message/stream watermark and as a fallback when no durable message usage
|
|
20
|
+
* was persisted (e.g. an interrupted attempt).
|
|
21
|
+
*
|
|
15
22
|
* DSH TokenUsage: {inputTokens, outputTokens, cacheReadTokens?, cacheWriteTokens?}
|
|
16
23
|
* → ResultEvent.usage: input_tokens/output_tokens/cache_read_tokens/
|
|
17
24
|
* cache_creation_tokens (cacheWrite = cache creation) + total_tokens sum.
|
|
18
25
|
*/
|
|
19
26
|
import type { AgentEvent, ResultEvent } from '../types.js';
|
|
20
|
-
import type { DshSessionEvent, DshTokenUsage } from './types.js';
|
|
27
|
+
import type { DshAssistantStreamFrame, DshSessionEvent, DshTokenUsage } from './types.js';
|
|
28
|
+
/** 本 turn 各采样用量之和(input 是非缓存输入,与 ccusage/ResultEvent 口径一致)。 */
|
|
29
|
+
interface UsageTotals {
|
|
30
|
+
input: number;
|
|
31
|
+
output: number;
|
|
32
|
+
cacheRead: number;
|
|
33
|
+
cacheWrite: number;
|
|
34
|
+
}
|
|
21
35
|
export declare class DshTranslator {
|
|
22
|
-
|
|
23
|
-
private
|
|
36
|
+
/** 本 turn 累计用量:DSH 每次模型调用(step)一条 usage,工具往返 = 多条。 */
|
|
37
|
+
private turnUsage;
|
|
38
|
+
/** 末次采样的 usage:上下文水位,或只有 live chunk 时的兜底值。 */
|
|
39
|
+
private lastUsage?;
|
|
40
|
+
/** 是否收到过 durable assistant/message.usage(唯一累加来源)。 */
|
|
41
|
+
private hasMessageUsage;
|
|
42
|
+
/** 是否只收到过 live usage chunk(无 durable message usage 时的兜底)。 */
|
|
43
|
+
private hasStreamUsage;
|
|
44
|
+
/** 当前 attempt 的 id(start 帧携带,chunk 帧也带;断开重连重放时不变)。 */
|
|
45
|
+
private attemptId;
|
|
46
|
+
/** Translate one live assistant-stream chunk into AgentEvents. */
|
|
47
|
+
streamFrameToAgentEvents(frame: DshAssistantStreamFrame, _sessionId: string): AgentEvent[];
|
|
48
|
+
/**
|
|
49
|
+
* Record durable per-step usage — the accumulation source. assistant/message
|
|
50
|
+
* carries exactly one usage per model call and is seq-deduped by the client,
|
|
51
|
+
* so summing it gives the turn total without double counting.
|
|
52
|
+
*/
|
|
53
|
+
private recordMessageUsage;
|
|
54
|
+
/**
|
|
55
|
+
* Record a live usage chunk. Never summed: the same step also arrives as a
|
|
56
|
+
* durable assistant/message.usage, so summing both would double count. It only
|
|
57
|
+
* refreshes the watermark and covers a turn whose messages never persisted.
|
|
58
|
+
*/
|
|
59
|
+
private recordStreamUsage;
|
|
60
|
+
private chunkToAgentEvents;
|
|
24
61
|
/** Translate one session event into AgentEvents (may be empty). */
|
|
25
62
|
eventToAgentEvents(ev: DshSessionEvent, sessionId: string): AgentEvent[];
|
|
26
63
|
}
|
|
27
64
|
/** Map DSH TokenUsage → ResultEvent.usage (cacheWrite = cache creation). */
|
|
28
65
|
export declare function mapUsage(u: DshTokenUsage): NonNullable<ResultEvent['usage']>;
|
|
66
|
+
/** Map a turn's summed usage → ResultEvent.usage (cacheWrite = cache creation). */
|
|
67
|
+
export declare function mapAggregatedUsage(t: UsageTotals): NonNullable<ResultEvent['usage']>;
|
|
68
|
+
export {};
|
|
@@ -12,13 +12,96 @@
|
|
|
12
12
|
* completed → success; aborted/interrupted → interrupted;
|
|
13
13
|
* blocked/error/max-tokens → error (errorMessage carries the reason)
|
|
14
14
|
*
|
|
15
|
+
* Token accounting (ccusage 口径, AGENTS.md §9.21): DSH emits ONE usage per model
|
|
16
|
+
* call (per step) — a tool-looping turn has several `assistant/message` events.
|
|
17
|
+
* The result event therefore carries **the sum of the turn's samplings**, not the
|
|
18
|
+
* last one (codex `token_count` has the same shape). `lastUsage` is kept only as
|
|
19
|
+
* the message/stream watermark and as a fallback when no durable message usage
|
|
20
|
+
* was persisted (e.g. an interrupted attempt).
|
|
21
|
+
*
|
|
15
22
|
* DSH TokenUsage: {inputTokens, outputTokens, cacheReadTokens?, cacheWriteTokens?}
|
|
16
23
|
* → ResultEvent.usage: input_tokens/output_tokens/cache_read_tokens/
|
|
17
24
|
* cache_creation_tokens (cacheWrite = cache creation) + total_tokens sum.
|
|
18
25
|
*/
|
|
19
26
|
export class DshTranslator {
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
/** 本 turn 累计用量:DSH 每次模型调用(step)一条 usage,工具往返 = 多条。 */
|
|
28
|
+
turnUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
29
|
+
/** 末次采样的 usage:上下文水位,或只有 live chunk 时的兜底值。 */
|
|
30
|
+
lastUsage;
|
|
31
|
+
/** 是否收到过 durable assistant/message.usage(唯一累加来源)。 */
|
|
32
|
+
hasMessageUsage = false;
|
|
33
|
+
/** 是否只收到过 live usage chunk(无 durable message usage 时的兜底)。 */
|
|
34
|
+
hasStreamUsage = false;
|
|
35
|
+
/** 当前 attempt 的 id(start 帧携带,chunk 帧也带;断开重连重放时不变)。 */
|
|
36
|
+
attemptId = '0';
|
|
37
|
+
/** Translate one live assistant-stream chunk into AgentEvents. */
|
|
38
|
+
streamFrameToAgentEvents(frame, _sessionId) {
|
|
39
|
+
if (frame.type === 'start') {
|
|
40
|
+
if (frame.attemptId)
|
|
41
|
+
this.attemptId = frame.attemptId;
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
if (frame.type !== 'chunk' || !frame.chunk)
|
|
45
|
+
return [];
|
|
46
|
+
return this.chunkToAgentEvents(frame.chunk, frame.time ?? Date.now(), frame.attemptId ?? this.attemptId);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Record durable per-step usage — the accumulation source. assistant/message
|
|
50
|
+
* carries exactly one usage per model call and is seq-deduped by the client,
|
|
51
|
+
* so summing it gives the turn total without double counting.
|
|
52
|
+
*/
|
|
53
|
+
recordMessageUsage(usage) {
|
|
54
|
+
this.turnUsage.input += usage.inputTokens ?? 0;
|
|
55
|
+
this.turnUsage.output += usage.outputTokens ?? 0;
|
|
56
|
+
this.turnUsage.cacheRead += usage.cacheReadTokens ?? 0;
|
|
57
|
+
this.turnUsage.cacheWrite += usage.cacheWriteTokens ?? 0;
|
|
58
|
+
this.lastUsage = usage;
|
|
59
|
+
this.hasMessageUsage = true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Record a live usage chunk. Never summed: the same step also arrives as a
|
|
63
|
+
* durable assistant/message.usage, so summing both would double count. It only
|
|
64
|
+
* refreshes the watermark and covers a turn whose messages never persisted.
|
|
65
|
+
*/
|
|
66
|
+
recordStreamUsage(usage) {
|
|
67
|
+
this.lastUsage = usage;
|
|
68
|
+
this.hasStreamUsage = true;
|
|
69
|
+
}
|
|
70
|
+
chunkToAgentEvents(chunk, time, attemptId = '0') {
|
|
71
|
+
if (chunk.type === 'usage' && chunk.usage && typeof chunk.usage === 'object') {
|
|
72
|
+
this.recordStreamUsage(chunk.usage);
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const ts = new Date(time).toISOString();
|
|
76
|
+
if (chunk.type === 'text-delta' && typeof chunk.text === 'string' && chunk.text) {
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
type: 'assistant',
|
|
80
|
+
message: { content: [{ type: 'text', text: chunk.text }] },
|
|
81
|
+
timestamp: ts,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
if (chunk.type === 'reasoning-delta' && typeof chunk.text === 'string' && chunk.text) {
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
type: 'assistant',
|
|
89
|
+
message: {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: 'thinking',
|
|
93
|
+
thinking: chunk.text,
|
|
94
|
+
// 同一 attempt 的同一 reasoning block:token delta 共享 itemId。
|
|
95
|
+
itemId: `r:${attemptId}:${chunk.index}`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
timestamp: ts,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
22
105
|
/** Translate one session event into AgentEvents (may be empty). */
|
|
23
106
|
eventToAgentEvents(ev, sessionId) {
|
|
24
107
|
switch (ev.type) {
|
|
@@ -26,40 +109,21 @@ export class DshTranslator {
|
|
|
26
109
|
const chunk = ev.data.chunk;
|
|
27
110
|
if (!chunk)
|
|
28
111
|
return [];
|
|
29
|
-
|
|
30
|
-
if (chunk.type === 'text-delta' && chunk.text) {
|
|
31
|
-
return [
|
|
32
|
-
{
|
|
33
|
-
type: 'assistant',
|
|
34
|
-
message: { content: [{ type: 'text', text: chunk.text }] },
|
|
35
|
-
timestamp: ts,
|
|
36
|
-
},
|
|
37
|
-
];
|
|
38
|
-
}
|
|
39
|
-
if (chunk.type === 'reasoning-delta' && chunk.text) {
|
|
40
|
-
return [
|
|
41
|
-
{
|
|
42
|
-
type: 'assistant',
|
|
43
|
-
message: { content: [{ type: 'thinking', thinking: chunk.text }] },
|
|
44
|
-
timestamp: ts,
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
}
|
|
48
|
-
return [];
|
|
112
|
+
return this.chunkToAgentEvents(chunk, ev.time);
|
|
49
113
|
}
|
|
50
114
|
case 'assistant/message': {
|
|
51
115
|
const raw = ev.data.usage;
|
|
52
116
|
if (raw && typeof raw === 'object') {
|
|
53
|
-
this.
|
|
54
|
-
this.hasUsage = true;
|
|
117
|
+
this.recordMessageUsage(raw);
|
|
55
118
|
}
|
|
56
119
|
return [];
|
|
57
120
|
}
|
|
58
121
|
case 'turn/end': {
|
|
59
|
-
const
|
|
122
|
+
const turnReason = ev.data.reason;
|
|
123
|
+
const reason = turnReason?.kind;
|
|
60
124
|
const subtype = reason === 'completed'
|
|
61
125
|
? 'success'
|
|
62
|
-
: reason === 'aborted' || reason === 'interrupted'
|
|
126
|
+
: reason === 'aborted' || reason === 'interrupted' || reason === 'forked'
|
|
63
127
|
? 'interrupted'
|
|
64
128
|
: 'error';
|
|
65
129
|
const result = {
|
|
@@ -68,11 +132,19 @@ export class DshTranslator {
|
|
|
68
132
|
session_id: sessionId,
|
|
69
133
|
timestamp: new Date(ev.time).toISOString(),
|
|
70
134
|
};
|
|
71
|
-
if (this.
|
|
72
|
-
|
|
135
|
+
if (this.hasMessageUsage) {
|
|
136
|
+
// 本 turn 各采样之和(多步工具往返 = 多条 assistant/message)。
|
|
137
|
+
result.usage = mapAggregatedUsage(this.turnUsage);
|
|
138
|
+
}
|
|
139
|
+
else if (this.hasStreamUsage && this.lastUsage) {
|
|
140
|
+
// 只有 live usage chunk(消息未落盘,如启动期中断):退回末次采样。
|
|
141
|
+
result.usage = mapUsage(this.lastUsage);
|
|
73
142
|
}
|
|
74
143
|
if (subtype === 'error') {
|
|
75
|
-
|
|
144
|
+
const detail = turnReason?.error?.message;
|
|
145
|
+
result.errorMessage = detail
|
|
146
|
+
? `turn ended with reason: ${reason}: ${detail}`
|
|
147
|
+
: `turn ended with reason: ${reason}`;
|
|
76
148
|
}
|
|
77
149
|
return [result];
|
|
78
150
|
}
|
|
@@ -83,16 +155,21 @@ export class DshTranslator {
|
|
|
83
155
|
}
|
|
84
156
|
/** Map DSH TokenUsage → ResultEvent.usage (cacheWrite = cache creation). */
|
|
85
157
|
export function mapUsage(u) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
158
|
+
return mapAggregatedUsage({
|
|
159
|
+
input: u.inputTokens ?? 0,
|
|
160
|
+
output: u.outputTokens ?? 0,
|
|
161
|
+
cacheRead: u.cacheReadTokens ?? 0,
|
|
162
|
+
cacheWrite: u.cacheWriteTokens ?? 0,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** Map a turn's summed usage → ResultEvent.usage (cacheWrite = cache creation). */
|
|
166
|
+
export function mapAggregatedUsage(t) {
|
|
90
167
|
return {
|
|
91
|
-
input_tokens: input,
|
|
92
|
-
output_tokens: output,
|
|
93
|
-
cache_read_tokens: cacheRead,
|
|
94
|
-
cache_creation_tokens: cacheWrite,
|
|
95
|
-
total_tokens: input + output + cacheRead + cacheWrite,
|
|
168
|
+
input_tokens: t.input,
|
|
169
|
+
output_tokens: t.output,
|
|
170
|
+
cache_read_tokens: t.cacheRead,
|
|
171
|
+
cache_creation_tokens: t.cacheWrite,
|
|
172
|
+
total_tokens: t.input + t.output + t.cacheRead + t.cacheWrite,
|
|
96
173
|
};
|
|
97
174
|
}
|
|
98
175
|
//# sourceMappingURL=translator.js.map
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DSH wire type shapes (DeepSeek Harness HTTP proxy contract).
|
|
3
3
|
*
|
|
4
|
-
* Source of truth:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Source of truth: @deepseek-ai/dsh-client-connection (unary envelope),
|
|
5
|
+
* @deepseek-ai/dsh-api-gateway (Remote stream mux) and
|
|
6
|
+
* @deepseek-ai/dsh-api-session-controller (session Remote methods) in DSH
|
|
7
|
+
* 0.2.x. Only the fields DshClient/DshRunner/DshSessionReader consume are
|
|
8
|
+
* declared here.
|
|
7
9
|
*
|
|
8
10
|
* Unary: POST /api/<method>, body `{type:'client-request',rpcId,method,payload}`,
|
|
9
11
|
* response `{type:'server-response',rpcId,result:{ok:true,value}|{ok:false,error}}`.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
+
* Auth: a signed `dsh-auth-*` cookie is required on every `/api/*` request and
|
|
13
|
+
* the `/api/remote.mux` WebSocket. The v1 cookie is minted from DSH's durable
|
|
14
|
+
* browser-session secret, or bootstrapped with `GET /?token=<launch token>`.
|
|
15
|
+
* Stream: WebSocket `/api/remote.mux` logical streams
|
|
16
|
+
* `{type:'open',streamId,endpoint:'session/follow',payload}` →
|
|
17
|
+
* `{type:'item',streamId,value}` / `{type:'end',streamId}`.
|
|
12
18
|
*/
|
|
13
19
|
/** One SessionEvent entry (core SessionEvent<T> — seq/time/data discriminated on type). */
|
|
14
20
|
export interface DshSessionEvent {
|
|
@@ -16,6 +22,9 @@ export interface DshSessionEvent {
|
|
|
16
22
|
seq: number;
|
|
17
23
|
time: number;
|
|
18
24
|
data: Record<string, unknown>;
|
|
25
|
+
ignorable?: true;
|
|
26
|
+
sourceEventSeqs?: unknown;
|
|
27
|
+
surfaceOp?: unknown;
|
|
19
28
|
}
|
|
20
29
|
/** TokenUsage from assistant/message.usage (dsh-llm TokenUsage). */
|
|
21
30
|
export interface DshTokenUsage {
|
|
@@ -25,31 +34,43 @@ export interface DshTokenUsage {
|
|
|
25
34
|
cacheWriteTokens?: number;
|
|
26
35
|
reasoningTokens?: number;
|
|
27
36
|
}
|
|
28
|
-
/**
|
|
37
|
+
/** Projection block returned by session/list / session/projections / follow. */
|
|
38
|
+
export interface DshProjections {
|
|
39
|
+
kind?: 'cached' | 'sequenced';
|
|
40
|
+
asOfSeq: number;
|
|
41
|
+
values: {
|
|
42
|
+
agentPreset?: string | null;
|
|
43
|
+
title?: string | null;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** SessionSummary row from session/list items[]. */
|
|
29
48
|
export interface DshSessionSummary {
|
|
30
49
|
sessionId: string;
|
|
31
50
|
updatedAt: number;
|
|
32
51
|
running: boolean;
|
|
33
52
|
blank: boolean;
|
|
34
53
|
cwd?: string;
|
|
35
|
-
/**
|
|
54
|
+
/** Legacy top-level preset (pre-0.2 DSH). */
|
|
36
55
|
agentPreset?: string;
|
|
56
|
+
/** 0.2.x exposes preset/title through projections. */
|
|
57
|
+
projections?: DshProjections;
|
|
37
58
|
}
|
|
38
|
-
/** session
|
|
59
|
+
/** session/list → value. */
|
|
39
60
|
export interface DshSessionListValue {
|
|
40
61
|
items: DshSessionSummary[];
|
|
41
62
|
}
|
|
42
|
-
/** session
|
|
43
|
-
export interface
|
|
63
|
+
/** session/page → value.records[]. */
|
|
64
|
+
export interface DshPageRecord {
|
|
44
65
|
event: DshSessionEvent;
|
|
45
66
|
view?: unknown;
|
|
46
67
|
}
|
|
47
|
-
/** session
|
|
48
|
-
export interface
|
|
49
|
-
|
|
68
|
+
/** session/page → value. */
|
|
69
|
+
export interface DshSessionPageValue {
|
|
70
|
+
records: DshPageRecord[];
|
|
50
71
|
hasMore: boolean;
|
|
51
72
|
}
|
|
52
|
-
/** One model entry inside a provider group (
|
|
73
|
+
/** One model entry inside a provider group (session/modelCatalog / session/selectModel groups[].models[]). */
|
|
53
74
|
export interface DshCatalogModel {
|
|
54
75
|
id: string;
|
|
55
76
|
name: string;
|
|
@@ -62,14 +83,16 @@ export interface DshCatalogModel {
|
|
|
62
83
|
defaultEffort?: string;
|
|
63
84
|
};
|
|
64
85
|
}
|
|
65
|
-
/** One provider group of the model catalog (
|
|
86
|
+
/** One provider group of the model catalog (session/modelCatalog groups[]). */
|
|
66
87
|
export interface DshModelGroup {
|
|
67
88
|
id: string;
|
|
68
89
|
name: string;
|
|
69
90
|
models: DshCatalogModel[];
|
|
70
91
|
}
|
|
71
|
-
/**
|
|
92
|
+
/** session/modelCatalog → value. */
|
|
72
93
|
export interface DshModelCatalogValue {
|
|
94
|
+
default?: DshModelSelection;
|
|
95
|
+
routableProviders?: string[];
|
|
73
96
|
groups: DshModelGroup[];
|
|
74
97
|
failures?: Array<{
|
|
75
98
|
id: string;
|
|
@@ -77,13 +100,13 @@ export interface DshModelCatalogValue {
|
|
|
77
100
|
message: string;
|
|
78
101
|
}>;
|
|
79
102
|
}
|
|
80
|
-
/** Current model selection (session
|
|
103
|
+
/** Current model selection (session/selectModel current / session/selectModel selected). */
|
|
81
104
|
export interface DshModelSelection {
|
|
82
105
|
provider: string;
|
|
83
106
|
model: string;
|
|
84
107
|
reasoningEffort?: string;
|
|
85
108
|
}
|
|
86
|
-
/** One preset row of
|
|
109
|
+
/** One preset row of agentPresets/list. */
|
|
87
110
|
export interface DshPresetEntry {
|
|
88
111
|
id: string;
|
|
89
112
|
trust: 'system' | 'user';
|
|
@@ -92,28 +115,167 @@ export interface DshPresetEntry {
|
|
|
92
115
|
description?: string;
|
|
93
116
|
broken?: string;
|
|
94
117
|
}
|
|
95
|
-
/**
|
|
118
|
+
/** agentPresets/list → value. */
|
|
96
119
|
export interface DshPresetListValue {
|
|
97
120
|
presets: DshPresetEntry[];
|
|
98
|
-
authorable
|
|
99
|
-
hasDocument
|
|
121
|
+
authorable?: boolean;
|
|
122
|
+
hasDocument?: boolean;
|
|
123
|
+
}
|
|
124
|
+
/** One permission preset from `permissionPresets/catalog`. */
|
|
125
|
+
export interface DshPermissionPresetOption {
|
|
126
|
+
value: string;
|
|
127
|
+
name: string;
|
|
128
|
+
description?: string;
|
|
129
|
+
}
|
|
130
|
+
/** `permissionPresets/catalog` → value. */
|
|
131
|
+
export interface DshPermissionCatalogValue {
|
|
132
|
+
options: DshPermissionPresetOption[];
|
|
133
|
+
/** Presets that may be selected as the default for future sessions. */
|
|
134
|
+
defaultOptions: DshPermissionPresetOption[];
|
|
135
|
+
defaultPreset: string;
|
|
136
|
+
}
|
|
137
|
+
/** One durable event record inside a follow snapshot / event frame. */
|
|
138
|
+
export interface DshFollowRecord {
|
|
139
|
+
type: 'event';
|
|
140
|
+
event: DshSessionEvent;
|
|
141
|
+
}
|
|
142
|
+
/** Opening frame of `session/follow`. */
|
|
143
|
+
export interface DshFollowSnapshot {
|
|
144
|
+
type: 'snapshot';
|
|
145
|
+
header?: Record<string, unknown>;
|
|
146
|
+
cursor: number;
|
|
147
|
+
records: DshFollowRecord[];
|
|
148
|
+
hasMore?: boolean;
|
|
149
|
+
projections?: DshProjections;
|
|
150
|
+
}
|
|
151
|
+
/** Assistant stream chunk emitted while the model is still generating. */
|
|
152
|
+
export type DshStreamChunk = {
|
|
153
|
+
type: 'text-delta';
|
|
154
|
+
index: number;
|
|
155
|
+
text: string;
|
|
156
|
+
} | {
|
|
157
|
+
type: 'reasoning-delta';
|
|
158
|
+
index: number;
|
|
159
|
+
text: string;
|
|
160
|
+
} | {
|
|
161
|
+
type: 'usage';
|
|
162
|
+
usage: DshTokenUsage;
|
|
163
|
+
} | {
|
|
164
|
+
type: string;
|
|
165
|
+
[key: string]: unknown;
|
|
166
|
+
};
|
|
167
|
+
/** One `assistant-stream` item of the follow stream. */
|
|
168
|
+
export interface DshAssistantStreamFrame {
|
|
169
|
+
type: 'start' | 'chunk' | 'end';
|
|
170
|
+
attemptId?: string;
|
|
171
|
+
revision?: number;
|
|
172
|
+
index?: number;
|
|
173
|
+
time?: number;
|
|
174
|
+
chunk?: DshStreamChunk;
|
|
175
|
+
outcome?: Record<string, unknown>;
|
|
176
|
+
}
|
|
177
|
+
/** First item of the Gateway-owned `$events` stream. */
|
|
178
|
+
export interface DshRemoteEventReadyFrame {
|
|
179
|
+
type: 'ready';
|
|
180
|
+
clientId: string;
|
|
181
|
+
host: {
|
|
182
|
+
home: string;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** One pending Agent-scoped approval waterfall delivered by `$events`. */
|
|
186
|
+
export interface DshRemoteEventWaterfallFrame {
|
|
187
|
+
type: 'waterfall';
|
|
188
|
+
event: string;
|
|
189
|
+
eventId: string;
|
|
190
|
+
agentId: string;
|
|
191
|
+
request: Record<string, unknown>;
|
|
192
|
+
}
|
|
193
|
+
/** Cancellation of a previously delivered waterfall. */
|
|
194
|
+
export interface DshRemoteEventCancelFrame {
|
|
195
|
+
type: 'cancel';
|
|
196
|
+
eventId: string;
|
|
197
|
+
}
|
|
198
|
+
/** Ordinary forwarded notification (not used by the DSH approval path). */
|
|
199
|
+
export interface DshRemoteEventEmitFrame {
|
|
200
|
+
type: 'emit';
|
|
201
|
+
event: string;
|
|
202
|
+
args: unknown[];
|
|
100
203
|
}
|
|
101
|
-
/**
|
|
102
|
-
export
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
204
|
+
/** One item of the Gateway-owned `$events` logical stream. */
|
|
205
|
+
export type DshRemoteEventFrame = DshRemoteEventReadyFrame | DshRemoteEventWaterfallFrame | DshRemoteEventCancelFrame | DshRemoteEventEmitFrame;
|
|
206
|
+
/** Answer accepted by `$events/result` for one approval waterfall. */
|
|
207
|
+
export type DshRemoteEventOutcome = {
|
|
208
|
+
kind: 'next';
|
|
209
|
+
} | {
|
|
210
|
+
kind: 'result';
|
|
211
|
+
value?: unknown;
|
|
212
|
+
} | {
|
|
213
|
+
kind: 'rejected';
|
|
214
|
+
error: {
|
|
215
|
+
name: string;
|
|
216
|
+
message: string;
|
|
217
|
+
code?: string;
|
|
218
|
+
details?: unknown;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
/** Approval-related items yielded while consuming `$events`. */
|
|
222
|
+
export type DshApprovalStreamItem = {
|
|
223
|
+
kind: 'ready';
|
|
224
|
+
clientId: string;
|
|
225
|
+
} | {
|
|
226
|
+
kind: 'request';
|
|
227
|
+
eventId: string;
|
|
228
|
+
clientId: string;
|
|
229
|
+
toolName: string;
|
|
230
|
+
callId?: string;
|
|
231
|
+
reason?: string;
|
|
232
|
+
} | {
|
|
233
|
+
kind: 'cancelled';
|
|
234
|
+
eventId: string;
|
|
235
|
+
} | {
|
|
236
|
+
kind: 'unavailable';
|
|
237
|
+
message: string;
|
|
238
|
+
};
|
|
239
|
+
/** One value yielded by a `session/follow` logical stream. */
|
|
240
|
+
export type DshFollowFrame = DshFollowSnapshot | {
|
|
241
|
+
type: 'event';
|
|
242
|
+
event: DshSessionEvent;
|
|
243
|
+
} | {
|
|
244
|
+
type: 'assistant-stream';
|
|
245
|
+
frame: DshAssistantStreamFrame;
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Normalized `commands/execute` result.
|
|
249
|
+
*
|
|
250
|
+
* DSH's wire value is `CommandExecution { commandId, result: CommandResult }`.
|
|
251
|
+
* The client flattens that envelope so every caller (compact, permission
|
|
252
|
+
* alignment) sees one stable shape. The RPC settles only after the command
|
|
253
|
+
* handler finishes — for `/compact` that means after the compaction completes.
|
|
254
|
+
* `kind:'error'` is an expected outcome (idle-only violation, no compactable
|
|
255
|
+
* history, summary failure), not a transport failure, so callers must surface
|
|
256
|
+
* `text` instead of throwing. `undefined` means the line was not admitted as a
|
|
257
|
+
* known command at all (admission miss logs nothing and runs nothing).
|
|
258
|
+
*/
|
|
259
|
+
export interface DshCommandResult {
|
|
260
|
+
commandId?: string;
|
|
261
|
+
kind?: 'success' | 'error';
|
|
262
|
+
text?: string;
|
|
263
|
+
sourceEventSeq?: number;
|
|
107
264
|
}
|
|
108
265
|
/** Unified per-session subscription item: a session event or an approval notice. */
|
|
109
266
|
export type DshStreamItem = {
|
|
110
267
|
kind: 'event';
|
|
111
268
|
sessionId: string;
|
|
112
269
|
event: DshSessionEvent;
|
|
270
|
+
} | {
|
|
271
|
+
kind: 'assistant-stream';
|
|
272
|
+
sessionId: string;
|
|
273
|
+
frame: DshAssistantStreamFrame;
|
|
113
274
|
} | {
|
|
114
275
|
kind: 'approval';
|
|
115
276
|
sessionId: string;
|
|
116
277
|
approvalId: string;
|
|
117
278
|
toolName: string;
|
|
279
|
+
callId?: string;
|
|
118
280
|
reason?: string;
|
|
119
281
|
};
|
package/dist/runner/dsh/types.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DSH wire type shapes (DeepSeek Harness HTTP proxy contract).
|
|
3
3
|
*
|
|
4
|
-
* Source of truth:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Source of truth: @deepseek-ai/dsh-client-connection (unary envelope),
|
|
5
|
+
* @deepseek-ai/dsh-api-gateway (Remote stream mux) and
|
|
6
|
+
* @deepseek-ai/dsh-api-session-controller (session Remote methods) in DSH
|
|
7
|
+
* 0.2.x. Only the fields DshClient/DshRunner/DshSessionReader consume are
|
|
8
|
+
* declared here.
|
|
7
9
|
*
|
|
8
10
|
* Unary: POST /api/<method>, body `{type:'client-request',rpcId,method,payload}`,
|
|
9
11
|
* response `{type:'server-response',rpcId,result:{ok:true,value}|{ok:false,error}}`.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
+
* Auth: a signed `dsh-auth-*` cookie is required on every `/api/*` request and
|
|
13
|
+
* the `/api/remote.mux` WebSocket. The v1 cookie is minted from DSH's durable
|
|
14
|
+
* browser-session secret, or bootstrapped with `GET /?token=<launch token>`.
|
|
15
|
+
* Stream: WebSocket `/api/remote.mux` logical streams
|
|
16
|
+
* `{type:'open',streamId,endpoint:'session/follow',payload}` →
|
|
17
|
+
* `{type:'item',streamId,value}` / `{type:'end',streamId}`.
|
|
12
18
|
*/
|
|
13
19
|
export {};
|
|
14
20
|
//# sourceMappingURL=types.js.map
|
|
@@ -294,6 +294,11 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
294
294
|
return this.compactBaselineRecords.length;
|
|
295
295
|
}
|
|
296
296
|
onCompactPromptFired(promptPromise, translator, sessionId, opts, baselineTotalCount) {
|
|
297
|
+
// 本轮归属锚点(代际 + client),所有 settle 回调落地前复核;
|
|
298
|
+
// 旧轮迟到的 prompt 结算不得写进新轮的共享队列。
|
|
299
|
+
const generation = this.turnGeneration;
|
|
300
|
+
const client = this.currentClient;
|
|
301
|
+
const isCurrent = () => this.isCurrentTurn(generation, client);
|
|
297
302
|
const compactionReader = this.sessionReader.readCompactionRecords;
|
|
298
303
|
// Derived from the pre-fire snapshot captured by compactBaseline() —
|
|
299
304
|
// re-reading here would race the prompt fire and misclassify new records.
|
|
@@ -302,6 +307,8 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
302
307
|
// prompt 结算只供两样东西:成功 result 的翻译素材(stopReason/usage)与 §5.4
|
|
303
308
|
// 探针。终态判定不再挂在它后面。
|
|
304
309
|
const promptSettled = promptPromise.then((result) => {
|
|
310
|
+
if (!isCurrent())
|
|
311
|
+
return undefined;
|
|
305
312
|
this.promptSettled = true;
|
|
306
313
|
if (compactionReader) {
|
|
307
314
|
// §5.4 可选防线(日志探针):prompt settle 后既无新 begin 也无新
|
|
@@ -314,6 +321,8 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
314
321
|
}
|
|
315
322
|
return result;
|
|
316
323
|
}, (err) => {
|
|
324
|
+
if (!isCurrent())
|
|
325
|
+
return undefined;
|
|
317
326
|
promptRejected = true;
|
|
318
327
|
this.handlePromptRejected(err, translator, sessionId, 'compact');
|
|
319
328
|
return undefined;
|
|
@@ -321,8 +330,9 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
321
330
|
if (!compactionReader) {
|
|
322
331
|
// 读不到 compaction 记录(reader 未实现):沿用「prompt 结算即成功」的口径。
|
|
323
332
|
void promptSettled.then((result) => {
|
|
324
|
-
if (result)
|
|
325
|
-
|
|
333
|
+
if (!result || !isCurrent())
|
|
334
|
+
return;
|
|
335
|
+
this.pushEvents([translator.handlePromptResponse(sessionId, result)]);
|
|
326
336
|
});
|
|
327
337
|
return;
|
|
328
338
|
}
|
|
@@ -331,6 +341,8 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
331
341
|
// 用户拖到通用 turnIdleTimeout(默认 30min),拿到的还是一个语义泛化的 turn
|
|
332
342
|
// 超时,而不是「压缩状态未知 / 可重试」。
|
|
333
343
|
void this.waitForCompactionTerminal(compactionReader, opts, baselineTerminalCount).then(async (outcome) => {
|
|
344
|
+
if (!isCurrent())
|
|
345
|
+
return;
|
|
334
346
|
// outcome === 'stopped':不推 result,由 consumeTurn 的 interrupted 兜底。
|
|
335
347
|
if (outcome === 'stopped' || this.forceFinish || this.stopRequested)
|
|
336
348
|
return;
|
|
@@ -351,7 +363,7 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
351
363
|
const result = await promptSettled;
|
|
352
364
|
if (!result || promptRejected)
|
|
353
365
|
return; // handlePromptRejected 已产 error result
|
|
354
|
-
if (this.forceFinish || this.stopRequested)
|
|
366
|
+
if (!isCurrent() || this.forceFinish || this.stopRequested)
|
|
355
367
|
return;
|
|
356
368
|
this.pushEvents([translator.handlePromptResponse(sessionId, result)]);
|
|
357
369
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpencodeLogErrorMonitor: watches opencode's own log file for LLM stream
|
|
3
3
|
* errors of the active ACP session and reports them immediately.
|
|
4
4
|
*
|
|
5
|
-
* Why this exists: opencode ACP (v1.18.x) does NOT forward retry/error state
|
|
5
|
+
* Why this exists: opencode ACP (v1.18.x and v2.0.x) does NOT forward retry/error state
|
|
6
6
|
* to the client over the ACP protocol. Its session/update notifications are
|
|
7
7
|
* limited to content deltas (agent_message_chunk / agent_thought_chunk /
|
|
8
8
|
* tool_call / tool_call_update), usage and available-commands updates — there
|
|
@@ -34,7 +34,7 @@ export interface OpencodeLogErrorMonitorOptions {
|
|
|
34
34
|
* Resolve opencode's log file path from the XDG data dir
|
|
35
35
|
* (`$XDG_DATA_HOME/opencode/log/opencode.log`, default
|
|
36
36
|
* `~/.local/share/opencode/log/opencode.log`). Matches opencode's own
|
|
37
|
-
* data-dir layout (
|
|
37
|
+
* data-dir layout (verified against v1.18.x and v2.0.32 `debug paths`).
|
|
38
38
|
*/
|
|
39
39
|
export declare function resolveOpencodeLogPath(): string;
|
|
40
40
|
export declare class OpencodeLogErrorMonitor {
|