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
|
@@ -23,191 +23,193 @@
|
|
|
23
23
|
* → ResultEvent.usage: input_tokens/output_tokens/cache_read_tokens/
|
|
24
24
|
* cache_creation_tokens (cacheWrite = cache creation) + total_tokens sum.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
recordMessageUsage(usage) {
|
|
62
|
-
this.turnUsage.input += usage.inputTokens ?? 0;
|
|
63
|
-
this.turnUsage.output += usage.outputTokens ?? 0;
|
|
64
|
-
this.turnUsage.cacheRead += usage.cacheReadTokens ?? 0;
|
|
65
|
-
this.turnUsage.cacheWrite += usage.cacheWriteTokens ?? 0;
|
|
66
|
-
this.lastUsage = usage;
|
|
67
|
-
this.hasMessageUsage = true;
|
|
26
|
+
function newTurnState() {
|
|
27
|
+
return {
|
|
28
|
+
turnUsage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
29
|
+
hasMessageUsage: false,
|
|
30
|
+
hasStreamUsage: false,
|
|
31
|
+
attemptId: '0',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// --- 模块级状态操作原语 ---
|
|
35
|
+
/**
|
|
36
|
+
* Record durable per-step usage — the accumulation source. assistant/message
|
|
37
|
+
* carries exactly one usage per model call and is seq-deduped by the client,
|
|
38
|
+
* so summing it gives the turn total without double counting.
|
|
39
|
+
*/
|
|
40
|
+
function recordMessageUsage(state, usage) {
|
|
41
|
+
state.turnUsage.input += usage.inputTokens ?? 0;
|
|
42
|
+
state.turnUsage.output += usage.outputTokens ?? 0;
|
|
43
|
+
state.turnUsage.cacheRead += usage.cacheReadTokens ?? 0;
|
|
44
|
+
state.turnUsage.cacheWrite += usage.cacheWriteTokens ?? 0;
|
|
45
|
+
state.lastUsage = usage;
|
|
46
|
+
state.hasMessageUsage = true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Record a live usage chunk. Never summed: the same step also arrives as a
|
|
50
|
+
* durable assistant/message.usage, so summing both would double count. It only
|
|
51
|
+
* refreshes the watermark and covers a turn whose messages never persisted.
|
|
52
|
+
*/
|
|
53
|
+
function recordStreamUsage(state, usage) {
|
|
54
|
+
state.lastUsage = usage;
|
|
55
|
+
state.hasStreamUsage = true;
|
|
56
|
+
}
|
|
57
|
+
function chunkToAgentEvents(state, chunk, time, attemptId = '0') {
|
|
58
|
+
if (chunk.type === 'usage' && chunk.usage && typeof chunk.usage === 'object') {
|
|
59
|
+
recordStreamUsage(state, chunk.usage);
|
|
60
|
+
return [];
|
|
68
61
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
const ts = new Date(time).toISOString();
|
|
63
|
+
if (chunk.type === 'text-delta' && typeof chunk.text === 'string' && chunk.text) {
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
type: 'assistant',
|
|
67
|
+
message: { content: [{ type: 'text', text: chunk.text }] },
|
|
68
|
+
timestamp: ts,
|
|
69
|
+
},
|
|
70
|
+
];
|
|
77
71
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
];
|
|
92
|
-
}
|
|
93
|
-
if (chunk.type === 'reasoning-delta' && typeof chunk.text === 'string' && chunk.text) {
|
|
94
|
-
return [
|
|
95
|
-
{
|
|
96
|
-
type: 'assistant',
|
|
97
|
-
message: {
|
|
98
|
-
content: [
|
|
99
|
-
{
|
|
100
|
-
type: 'thinking',
|
|
101
|
-
thinking: chunk.text,
|
|
102
|
-
// 同一 attempt 的同一 reasoning block:token delta 共享 itemId。
|
|
103
|
-
itemId: `r:${attemptId}:${chunk.index}`,
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
},
|
|
107
|
-
timestamp: ts,
|
|
72
|
+
if (chunk.type === 'reasoning-delta' && typeof chunk.text === 'string' && chunk.text) {
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
type: 'assistant',
|
|
76
|
+
message: {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'thinking',
|
|
80
|
+
thinking: chunk.text,
|
|
81
|
+
// 同一 attempt 的同一 reasoning block:token delta 共享 itemId。
|
|
82
|
+
itemId: `r:${attemptId}:${chunk.index}`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
108
85
|
},
|
|
109
|
-
|
|
110
|
-
|
|
86
|
+
timestamp: ts,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
/** Translate one live assistant-stream chunk into AgentEvents. */
|
|
93
|
+
function translateStreamFrame(state, frame, _sessionId) {
|
|
94
|
+
if (frame.type === 'start') {
|
|
95
|
+
if (frame.attemptId)
|
|
96
|
+
state.attemptId = frame.attemptId;
|
|
111
97
|
return [];
|
|
112
98
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
99
|
+
if (frame.type !== 'chunk' || !frame.chunk)
|
|
100
|
+
return [];
|
|
101
|
+
return chunkToAgentEvents(state, frame.chunk, frame.time ?? Date.now(), frame.attemptId ?? state.attemptId);
|
|
102
|
+
}
|
|
103
|
+
function handleAssistantChunk(state, ev, _sessionId) {
|
|
104
|
+
const chunk = ev.data.chunk;
|
|
105
|
+
if (!chunk)
|
|
106
|
+
return [];
|
|
107
|
+
return chunkToAgentEvents(state, chunk, ev.time);
|
|
108
|
+
}
|
|
109
|
+
function handleAssistantMessage(state, ev, _sessionId) {
|
|
110
|
+
const raw = ev.data.usage;
|
|
111
|
+
if (raw && typeof raw === 'object') {
|
|
112
|
+
recordMessageUsage(state, raw);
|
|
113
|
+
}
|
|
114
|
+
// 每条 durable 消息都带完整正文;记最后一条非空的就是本轮回答。
|
|
115
|
+
const text = messageText(ev.data.message);
|
|
116
|
+
if (text.trim())
|
|
117
|
+
state.lastMessageText = text;
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
function handleToolCall(_state, ev, _sessionId) {
|
|
121
|
+
// DSH 把「模型发起调用」落成独立事件(name/arguments 是模型原始输出)。
|
|
122
|
+
// 这是卡片工具行的唯一来源:assistant/message 里只有完成后的正文块,
|
|
123
|
+
// 不含调用轨迹。
|
|
124
|
+
const call = ev.data;
|
|
125
|
+
const id = typeof call.callId === 'string' ? call.callId : '';
|
|
126
|
+
const name = typeof call.name === 'string' ? call.name : '';
|
|
127
|
+
if (!id || !name)
|
|
128
|
+
return [];
|
|
129
|
+
return [
|
|
130
|
+
{
|
|
131
|
+
type: 'assistant',
|
|
132
|
+
message: {
|
|
133
|
+
content: [{ type: 'tool_use', id, name, input: parseToolArguments(call.arguments) }],
|
|
134
|
+
},
|
|
135
|
+
timestamp: new Date(ev.time).toISOString(),
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
function handleToolResult(_state, ev, _sessionId) {
|
|
140
|
+
const data = ev.data;
|
|
141
|
+
const msg = data.message;
|
|
142
|
+
const toolCallId = typeof msg?.toolCallId === 'string' ? msg.toolCallId : '';
|
|
143
|
+
if (!toolCallId)
|
|
144
|
+
return [];
|
|
145
|
+
return [
|
|
146
|
+
{
|
|
147
|
+
type: 'user',
|
|
148
|
+
message: {
|
|
149
|
+
content: [
|
|
159
150
|
{
|
|
160
|
-
type: '
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
type: 'tool_result',
|
|
165
|
-
tool_use_id: toolCallId,
|
|
166
|
-
content: toolResultText(msg?.content, data.error),
|
|
167
|
-
is_error: msg?.isError === true,
|
|
168
|
-
},
|
|
169
|
-
],
|
|
170
|
-
},
|
|
171
|
-
timestamp: new Date(ev.time).toISOString(),
|
|
151
|
+
type: 'tool_result',
|
|
152
|
+
tool_use_id: toolCallId,
|
|
153
|
+
content: toolResultText(msg?.content, data.error),
|
|
154
|
+
is_error: msg?.isError === true,
|
|
172
155
|
},
|
|
173
|
-
]
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
timestamp: new Date(ev.time).toISOString(),
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
function handleTurnEnd(state, ev, sessionId) {
|
|
163
|
+
const turnReason = ev.data.reason;
|
|
164
|
+
const reason = turnReason?.kind;
|
|
165
|
+
const subtype = reason === 'completed'
|
|
166
|
+
? 'success'
|
|
167
|
+
: reason === 'aborted' || reason === 'interrupted' || reason === 'forked'
|
|
168
|
+
? 'interrupted'
|
|
169
|
+
: 'error';
|
|
170
|
+
const result = {
|
|
171
|
+
type: 'result',
|
|
172
|
+
subtype,
|
|
173
|
+
session_id: sessionId,
|
|
174
|
+
timestamp: new Date(ev.time).toISOString(),
|
|
175
|
+
};
|
|
176
|
+
if (state.hasMessageUsage) {
|
|
177
|
+
// 本 turn 各采样之和(多步工具往返 = 多条 assistant/message)。
|
|
178
|
+
result.usage = mapAggregatedUsage(state.turnUsage);
|
|
179
|
+
}
|
|
180
|
+
else if (state.hasStreamUsage && state.lastUsage) {
|
|
181
|
+
// 只有 live usage chunk(消息未落盘,如启动期中断):退回末次采样。
|
|
182
|
+
result.usage = mapUsage(state.lastUsage);
|
|
183
|
+
}
|
|
184
|
+
if (subtype === 'error') {
|
|
185
|
+
const detail = turnReason?.error?.message;
|
|
186
|
+
result.errorMessage = detail
|
|
187
|
+
? `turn ended with reason: ${reason}: ${detail}`
|
|
188
|
+
: `turn ended with reason: ${reason}`;
|
|
189
|
+
}
|
|
190
|
+
if (state.lastMessageText) {
|
|
191
|
+
result.finalAnswerText = state.lastMessageText;
|
|
192
|
+
}
|
|
193
|
+
return [result];
|
|
194
|
+
}
|
|
195
|
+
/** 事件类型 → handler。用 Map 而非对象字面量,避免 prototype 键命中。 */
|
|
196
|
+
const SESSION_EVENT_HANDLERS = new Map([
|
|
197
|
+
['assistant/chunk', handleAssistantChunk],
|
|
198
|
+
['assistant/message', handleAssistantMessage],
|
|
199
|
+
['tool/call', handleToolCall],
|
|
200
|
+
['tool/result', handleToolResult],
|
|
201
|
+
['turn/end', handleTurnEnd],
|
|
202
|
+
]);
|
|
203
|
+
export class DshTranslator {
|
|
204
|
+
state = newTurnState();
|
|
205
|
+
/** Translate one live assistant-stream chunk into AgentEvents. */
|
|
206
|
+
streamFrameToAgentEvents(frame, _sessionId) {
|
|
207
|
+
return translateStreamFrame(this.state, frame, _sessionId);
|
|
208
|
+
}
|
|
209
|
+
/** Translate one session event into AgentEvents (may be empty). */
|
|
210
|
+
eventToAgentEvents(ev, sessionId) {
|
|
211
|
+
const handler = SESSION_EVENT_HANDLERS.get(ev.type);
|
|
212
|
+
return handler ? handler(this.state, ev, sessionId) : [];
|
|
211
213
|
}
|
|
212
214
|
}
|
|
213
215
|
/**
|
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, AgentRunner, TurnStartedEvent, TurnDiffEvent, FinalAnswerEvent, NoticeEvent, SessionInfoEvent, ToolHint, ApprovalView, ApprovalRequestedEvent, ApprovalResolvedEvent, BackgroundJobRoster, BackgroundJobsEvent, GoalView, GoalStateEvent, } from './types.js';
|
|
18
|
+
export type { ResultEvent, PlanEvent, FileChangeEvent, AgentEvent, Runner, RunnerLifetime, AgentRunner, TurnStartedEvent, TurnDiffEvent, FinalAnswerEvent, NoticeEvent, SessionInfoEvent, ToolHint, ApprovalView, ApprovalRequestedEvent, ApprovalResolvedEvent, BackgroundJobRoster, BackgroundJobsEvent, AssistantEvent, RunStatusEvent, UserEvent, GoalView, GoalStateEvent, } from './types.js';
|
|
19
19
|
export { resolveAgentChoices } from './resolve-agent-choices.js';
|
|
20
20
|
export { syncAgentChoices } from './sync-agent-choices.js';
|
|
@@ -109,8 +109,7 @@ export declare class KimiAcpRunner extends BaseAcpRunner<KimiAcpTranslator> {
|
|
|
109
109
|
* before producing the result. There is NO total timeout — silence beyond
|
|
110
110
|
* compactIdleTimeoutMs yields 'unknown' (never a fake success). Keeps the
|
|
111
111
|
* connection alive while the background compaction finishes — otherwise
|
|
112
|
-
* dispose/exit kills it and the record never lands
|
|
113
|
-
* 2026-08-31 事故).
|
|
112
|
+
* dispose/exit kills it and the record never lands.
|
|
114
113
|
*/
|
|
115
114
|
protected compactBaseline(sessionId: string, opts: SpawnOptions): number;
|
|
116
115
|
protected onCompactPromptFired(promptPromise: Promise<SessionPromptResult>, translator: KimiAcpTranslator, sessionId: string, opts: SpawnOptions, baselineTotalCount: number): void;
|
|
@@ -73,6 +73,85 @@ function toAcpMode(mode) {
|
|
|
73
73
|
return 'yolo';
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// buildQuestionResponse 的模块级命名 handler(闭包搬出,显式传状态)
|
|
78
|
+
// =============================================================================
|
|
79
|
+
function nonEmptyString(v) {
|
|
80
|
+
return typeof v === 'string' && v.length > 0;
|
|
81
|
+
}
|
|
82
|
+
/** 单题答案 → content[qN](多选清洗空串、单选取首个)。 */
|
|
83
|
+
function collectOneAnswer(q, i, byIndex, content) {
|
|
84
|
+
const value = byIndex[i];
|
|
85
|
+
if (value === undefined)
|
|
86
|
+
return;
|
|
87
|
+
if (q.multiSelect) {
|
|
88
|
+
const values = Array.isArray(value) ? value : [value];
|
|
89
|
+
const cleaned = values.filter(nonEmptyString);
|
|
90
|
+
if (cleaned.length > 0)
|
|
91
|
+
content[`q${i}`] = cleaned;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const single = Array.isArray(value) ? value[0] : value;
|
|
95
|
+
if (typeof single === 'string' && single.length > 0)
|
|
96
|
+
content[`q${i}`] = single;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** elicitation form:{问题文本: 值} → 按问题顺序展开的 q0..qn content。 */
|
|
100
|
+
function elicitationContent(questions, answers) {
|
|
101
|
+
const byIndex = mapAnswersByIndex(questions, answers);
|
|
102
|
+
const content = {};
|
|
103
|
+
for (let i = 0; i < questions.length; i++) {
|
|
104
|
+
collectOneAnswer(questions[i], i, byIndex, content);
|
|
105
|
+
}
|
|
106
|
+
return content;
|
|
107
|
+
}
|
|
108
|
+
function elicitationResponse(action, pending, response) {
|
|
109
|
+
if (action === 'answer') {
|
|
110
|
+
const answers = response.answers;
|
|
111
|
+
const questions = pending.view.questions ?? [];
|
|
112
|
+
return { action: 'accept', content: elicitationContent(questions, answers ?? {}) };
|
|
113
|
+
}
|
|
114
|
+
if (action === 'decline')
|
|
115
|
+
return { action: 'decline' };
|
|
116
|
+
return { action: 'cancel' };
|
|
117
|
+
}
|
|
118
|
+
/** request_permission 兜底桥:按 label 回显 qN_opt_M optionId。 */
|
|
119
|
+
function selectedOptionId(options, label) {
|
|
120
|
+
for (const opt of options) {
|
|
121
|
+
if (/^q\d+_opt_\d+$/.test(opt.optionId) && opt.name === label)
|
|
122
|
+
return opt.optionId;
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
/** request_permission 兜底桥:找 qN_skip optionId。 */
|
|
127
|
+
function skipOptionId(options) {
|
|
128
|
+
for (const opt of options) {
|
|
129
|
+
if (/^q\d+_skip$/.test(opt.optionId))
|
|
130
|
+
return opt.optionId;
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
function cancelledOutcome() {
|
|
135
|
+
return { outcome: { outcome: 'cancelled' } };
|
|
136
|
+
}
|
|
137
|
+
function selectedOutcome(optionId) {
|
|
138
|
+
return { outcome: { outcome: 'selected', optionId } };
|
|
139
|
+
}
|
|
140
|
+
function permissionResponse(action, pending, response) {
|
|
141
|
+
if (action === 'answer') {
|
|
142
|
+
const answers = response.answers;
|
|
143
|
+
const first = pending.view.questions?.[0];
|
|
144
|
+
const value = first ? answers?.[first.question] : undefined;
|
|
145
|
+
const label = Array.isArray(value) ? value[0] : value;
|
|
146
|
+
const optionId = selectedOptionId(pending.options ?? [], label);
|
|
147
|
+
return optionId ? selectedOutcome(optionId) : cancelledOutcome();
|
|
148
|
+
}
|
|
149
|
+
if (action === 'decline') {
|
|
150
|
+
const skipId = skipOptionId(pending.options ?? []);
|
|
151
|
+
return skipId ? selectedOutcome(skipId) : cancelledOutcome();
|
|
152
|
+
}
|
|
153
|
+
return cancelledOutcome();
|
|
154
|
+
}
|
|
76
155
|
/**
|
|
77
156
|
* AskUserQuestion 回编(kind === 'question'):
|
|
78
157
|
* - elicitation form:answer → {action:'accept', content:{q0..qn}}(多选数组/
|
|
@@ -84,51 +163,9 @@ function toAcpMode(mode) {
|
|
|
84
163
|
*/
|
|
85
164
|
function buildQuestionResponse(action, pending, response) {
|
|
86
165
|
if (pending.proto === 'elicitation') {
|
|
87
|
-
|
|
88
|
-
const answers = response.answers;
|
|
89
|
-
const questions = pending.view.questions ?? [];
|
|
90
|
-
const byIndex = mapAnswersByIndex(questions, answers ?? {});
|
|
91
|
-
const content = {};
|
|
92
|
-
questions.forEach((q, i) => {
|
|
93
|
-
const value = byIndex[i];
|
|
94
|
-
if (value === undefined)
|
|
95
|
-
return;
|
|
96
|
-
if (q.multiSelect) {
|
|
97
|
-
const values = Array.isArray(value) ? value : [value];
|
|
98
|
-
const cleaned = values.filter((v) => typeof v === 'string' && v.length > 0);
|
|
99
|
-
if (cleaned.length > 0)
|
|
100
|
-
content[`q${i}`] = cleaned;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
const single = Array.isArray(value) ? value[0] : value;
|
|
104
|
-
if (typeof single === 'string' && single.length > 0)
|
|
105
|
-
content[`q${i}`] = single;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
return { action: 'accept', content };
|
|
109
|
-
}
|
|
110
|
-
if (action === 'decline')
|
|
111
|
-
return { action: 'decline' };
|
|
112
|
-
return { action: 'cancel' };
|
|
113
|
-
}
|
|
114
|
-
// request_permission 兜底桥(单题单选)
|
|
115
|
-
if (action === 'answer') {
|
|
116
|
-
const answers = response.answers;
|
|
117
|
-
const first = pending.view.questions?.[0];
|
|
118
|
-
const value = first ? answers?.[first.question] : undefined;
|
|
119
|
-
const label = Array.isArray(value) ? value[0] : value;
|
|
120
|
-
const option = (pending.options ?? []).find((opt) => /^q\d+_opt_\d+$/.test(opt.optionId) && opt.name === label);
|
|
121
|
-
if (option)
|
|
122
|
-
return { outcome: { outcome: 'selected', optionId: option.optionId } };
|
|
123
|
-
return { outcome: { outcome: 'cancelled' } };
|
|
166
|
+
return elicitationResponse(action, pending, response);
|
|
124
167
|
}
|
|
125
|
-
|
|
126
|
-
const skip = (pending.options ?? []).find((opt) => /^q\d+_skip$/.test(opt.optionId));
|
|
127
|
-
if (skip)
|
|
128
|
-
return { outcome: { outcome: 'selected', optionId: skip.optionId } };
|
|
129
|
-
return { outcome: { outcome: 'cancelled' } };
|
|
130
|
-
}
|
|
131
|
-
return { outcome: { outcome: 'cancelled' } };
|
|
168
|
+
return permissionResponse(action, pending, response);
|
|
132
169
|
}
|
|
133
170
|
// =============================================================================
|
|
134
171
|
// Runner
|
|
@@ -292,8 +329,7 @@ export class KimiAcpRunner extends BaseAcpRunner {
|
|
|
292
329
|
* before producing the result. There is NO total timeout — silence beyond
|
|
293
330
|
* compactIdleTimeoutMs yields 'unknown' (never a fake success). Keeps the
|
|
294
331
|
* connection alive while the background compaction finishes — otherwise
|
|
295
|
-
* dispose/exit kills it and the record never lands
|
|
296
|
-
* 2026-08-31 事故).
|
|
332
|
+
* dispose/exit kills it and the record never lands.
|
|
297
333
|
*/
|
|
298
334
|
compactBaseline(sessionId, opts) {
|
|
299
335
|
const compactionReader = this.sessionReader.readCompactionRecords;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* assistant deltas — see handleAgentMessageChunk), plus question elicitation.
|
|
10
10
|
*
|
|
11
11
|
* Wire envelope (source: kimi-code packages/acp-server/src/events-map.ts,
|
|
12
|
-
* verified
|
|
12
|
+
* verified against kimi 0.36.0):
|
|
13
13
|
* session/update params = {sessionId, update: {sessionUpdate: '<kind>', ...}}
|
|
14
14
|
* — the discriminator is `update.sessionUpdate`, NOT a nested `event.type`.
|
|
15
15
|
*
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* assistant deltas — see handleAgentMessageChunk), plus question elicitation.
|
|
10
10
|
*
|
|
11
11
|
* Wire envelope (source: kimi-code packages/acp-server/src/events-map.ts,
|
|
12
|
-
* verified
|
|
12
|
+
* verified against kimi 0.36.0):
|
|
13
13
|
* session/update params = {sessionId, update: {sessionUpdate: '<kind>', ...}}
|
|
14
14
|
* — the discriminator is `update.sessionUpdate`, NOT a nested `event.type`.
|
|
15
15
|
*
|
|
@@ -69,8 +69,7 @@ export class KimiAcpTranslator extends BaseAcpTranslator {
|
|
|
69
69
|
// snapshot and emit it as turn_diff. The card reducer's turn_diff path
|
|
70
70
|
// REPLACES the block in place (snapshot semantics). Emitting the full
|
|
71
71
|
// text as assistant/text (delta semantics) made the reducer append the
|
|
72
|
-
// full text to itself on every chunk — duplicated card output
|
|
73
|
-
// (2026-08-17 live regression, reproduced with 3 chunks).
|
|
72
|
+
// full text to itself on every chunk — duplicated card output.
|
|
74
73
|
const key = 'text';
|
|
75
74
|
const prev = this.textByItem.get(key) ?? '';
|
|
76
75
|
const next = prev + event.content.text;
|
|
@@ -130,7 +129,7 @@ export class KimiAcpTranslator extends BaseAcpTranslator {
|
|
|
130
129
|
getLogger().info(`[kimi-acp-translator] permission request without toolCall, auto-responding cancelled (requestId=${requestId})`);
|
|
131
130
|
return [];
|
|
132
131
|
}
|
|
133
|
-
// 命令恢复(
|
|
132
|
+
// 命令恢复(kimi 0.42.0):审批门在 tool.call.started
|
|
134
133
|
// 之前触发,request_permission 的 toolCall 只有 title("Bash")+ content
|
|
135
134
|
// 里一句截断摘要("Requesting approval to Running: <cmd>…"),无 rawInput;
|
|
136
135
|
// 完整命令只从审批前的 tool_call/tool_call_update 流式 args 累积文本拿到
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* 5xx/network errors retry in seconds and must be left to opencode's own
|
|
25
25
|
* retry loop (the idle watchdog remains the fallback for silent hangs).
|
|
26
26
|
*/
|
|
27
|
-
import
|
|
27
|
+
import fs from 'node:fs';
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
29
|
import { join } from 'node:path';
|
|
30
30
|
import { getLogger } from '../../../logger/index.js';
|
|
@@ -88,7 +88,7 @@ export class OpencodeLogErrorMonitor {
|
|
|
88
88
|
}
|
|
89
89
|
currentSize() {
|
|
90
90
|
try {
|
|
91
|
-
return statSync(this.logPath).size;
|
|
91
|
+
return fs.statSync(this.logPath).size;
|
|
92
92
|
}
|
|
93
93
|
catch (err) {
|
|
94
94
|
this.noteIoError(err);
|
|
@@ -124,14 +124,14 @@ export class OpencodeLogErrorMonitor {
|
|
|
124
124
|
if (size > this.offset) {
|
|
125
125
|
let chunk;
|
|
126
126
|
try {
|
|
127
|
-
const fd = openSync(this.logPath, 'r');
|
|
127
|
+
const fd = fs.openSync(this.logPath, 'r');
|
|
128
128
|
try {
|
|
129
129
|
const buf = Buffer.alloc(size - this.offset);
|
|
130
|
-
readSync(fd, buf, 0, buf.length, this.offset);
|
|
130
|
+
fs.readSync(fd, buf, 0, buf.length, this.offset);
|
|
131
131
|
chunk = buf.toString('utf8');
|
|
132
132
|
}
|
|
133
133
|
finally {
|
|
134
|
-
closeSync(fd);
|
|
134
|
+
fs.closeSync(fd);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
catch (err) {
|
|
@@ -95,7 +95,7 @@ export declare class OpencodeAcpRunner extends BaseAcpRunner<OpencodeAcpTranslat
|
|
|
95
95
|
* before failing the turn.
|
|
96
96
|
*
|
|
97
97
|
* 单次 attempt 自身还要等 opencode 侧超时(~10s),所以真实窗口 ≈ Σdelay +
|
|
98
|
-
* 每次的等待。
|
|
98
|
+
* 每次的等待。daemon 刚重启时 `/api/model` 会因模型目录拉取
|
|
99
99
|
* 超时连续 503 约 7 分钟,旧退避 0.5/1.5/3s(合计 ~45s)全落在这段里,4 次
|
|
100
100
|
* 重试无一成功。现在铺到 ~75s + 每次超时 ≈ 2 分钟,覆盖一次冷启动窗口,
|
|
101
101
|
* 又不至于让真故障的用户干等太久。
|
|
@@ -105,7 +105,7 @@ export declare class OpencodeAcpRunner extends BaseAcpRunner<OpencodeAcpTranslat
|
|
|
105
105
|
/**
|
|
106
106
|
* 用户可见文案。这里刻意**不**建议 `opencode service restart`:卡住的是
|
|
107
107
|
* opencode 的模型目录/位置服务冷启动,重启只会清掉刚热好的状态、重新回到
|
|
108
|
-
*
|
|
108
|
+
* 同一个窗口(照提示重启两次仍然失败,等几分钟自己好了)。
|
|
109
109
|
*/
|
|
110
110
|
protected enrichSessionSetupError(err: unknown): Error;
|
|
111
111
|
getStatusInfo(): AgentStatusInfo;
|