lark-remote 0.2.2 → 0.4.0
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 +33 -0
- package/README.en.md +8 -5
- package/README.md +10 -6
- package/dist/binder.d.ts +9 -16
- package/dist/binder.js +14 -40
- package/dist/bridge/final-usage.js +2 -2
- package/dist/bridge/inbound-media.js +7 -5
- package/dist/bridge/index.js +75 -75
- package/dist/clone.d.ts +139 -0
- package/dist/clone.js +415 -0
- package/dist/common/fs.js +1 -1
- package/dist/config/codex-config.js +79 -24
- package/dist/config/index.js +1 -1
- package/dist/config/kimi-config.js +16 -5
- package/dist/connector/index.d.ts +25 -0
- package/dist/connector/index.js +133 -50
- package/dist/error-classification.d.ts +7 -0
- package/dist/error-classification.js +37 -1
- package/dist/index.js +48 -15
- package/dist/platform/command.js +1 -1
- package/dist/platform/sleep-blocker.d.ts +17 -0
- package/dist/platform/sleep-blocker.js +73 -0
- package/dist/platform/terminator-win32.js +1 -1
- package/dist/restart.d.ts +20 -3
- package/dist/restart.js +42 -23
- package/dist/router/config/kimi.js +4 -3
- package/dist/router/index.d.ts +10 -0
- package/dist/router/index.js +44 -6
- package/dist/runner/common/acp/base-acp-runner.js +7 -2
- package/dist/runner/common/acp/base-translator.d.ts +19 -0
- package/dist/runner/common/acp/base-translator.js +82 -0
- package/dist/runner/common/acp/protocol-types.d.ts +16 -2
- package/dist/runner/common/acp/protocol-types.js +3 -0
- package/dist/runner/kimi/acp/translator.js +60 -4
- package/dist/session/session-store.d.ts +1 -1
- package/dist/session/session-store.js +1 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.js +1 -1
- package/package.json +2 -2
|
@@ -11,6 +11,36 @@ const KIND_TO_TOOL = {
|
|
|
11
11
|
search: 'Grep',
|
|
12
12
|
fetch: 'WebFetch',
|
|
13
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* Join the text payloads of a tool_call(/_update) content array
|
|
16
|
+
* (`[{type:'content', content:{type:'text', text}}]` entries). Returns
|
|
17
|
+
* undefined when no text entry exists (diff/terminal-only content).
|
|
18
|
+
*/
|
|
19
|
+
function extractToolCallContentText(content) {
|
|
20
|
+
if (!Array.isArray(content))
|
|
21
|
+
return undefined;
|
|
22
|
+
let text = '';
|
|
23
|
+
let found = false;
|
|
24
|
+
for (const entry of content) {
|
|
25
|
+
const e = entry;
|
|
26
|
+
if (e?.type === 'content' && e.content?.type === 'text' && typeof e.content.text === 'string') {
|
|
27
|
+
text += e.content.text;
|
|
28
|
+
found = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return found ? text : undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Best-effort stringify for tracked rawInput objects (never throws). */
|
|
34
|
+
function stringifyUnknown(value) {
|
|
35
|
+
if (typeof value === 'string')
|
|
36
|
+
return value;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.stringify(value) ?? String(value);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return String(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
14
44
|
// =============================================================================
|
|
15
45
|
// Translator base class
|
|
16
46
|
// =============================================================================
|
|
@@ -25,6 +55,16 @@ export class BaseAcpTranslator {
|
|
|
25
55
|
liveCostUsd;
|
|
26
56
|
/** Current turn id, set by produceTurnStarted. */
|
|
27
57
|
currentTurnId = '';
|
|
58
|
+
/**
|
|
59
|
+
* Latest streaming-args text per tool call (keyed by wire toolCallId).
|
|
60
|
+
* kimi ACP sends the permission request BEFORE tool.call.started (verified
|
|
61
|
+
* live against kimi 0.42.0, 2026-09-13): the request_permission toolCall
|
|
62
|
+
* carries only title + a truncated summary in content, NO rawInput — the
|
|
63
|
+
* full args only ever reach the client as accumulated text in the
|
|
64
|
+
* tool_call/tool_call_update streaming deltas. Tracking them here lets the
|
|
65
|
+
* approval handler correlate by toolCallId and recover the real command.
|
|
66
|
+
*/
|
|
67
|
+
toolCallArgsByCallId = new Map();
|
|
28
68
|
/**
|
|
29
69
|
* Handle a notification from the ACP server and return translated events.
|
|
30
70
|
* Wire envelope: {sessionId, update: {sessionUpdate: '<kind>', ...}} — the
|
|
@@ -191,6 +231,13 @@ export class BaseAcpTranslator {
|
|
|
191
231
|
// Keep raw string as-is
|
|
192
232
|
}
|
|
193
233
|
}
|
|
234
|
+
const argsText = extractToolCallContentText(event.content);
|
|
235
|
+
if (argsText !== undefined) {
|
|
236
|
+
this.trackToolCallArgs(event.toolCallId, argsText);
|
|
237
|
+
}
|
|
238
|
+
else if (event.rawInput !== undefined && event.rawInput !== null) {
|
|
239
|
+
this.trackToolCallArgs(event.toolCallId, stringifyUnknown(event.rawInput));
|
|
240
|
+
}
|
|
194
241
|
const mapped = event.kind ? KIND_TO_TOOL[event.kind] : undefined;
|
|
195
242
|
const name = mapped ?? event.title;
|
|
196
243
|
const summary = mapped && event.title && event.title !== name ? event.title : undefined;
|
|
@@ -218,6 +265,22 @@ export class BaseAcpTranslator {
|
|
|
218
265
|
handleToolCallUpdate(event, _sessionId) {
|
|
219
266
|
const isError = event.status === 'failed';
|
|
220
267
|
const content = this.extractToolResultContent(event);
|
|
268
|
+
// Terminal updates end the args-streaming phase — drop the tracked entry.
|
|
269
|
+
// In-progress delta text is cumulative (events-map.ts
|
|
270
|
+
// toolCallDeltaToSessionUpdate: content.text = accumulator.args), so the
|
|
271
|
+
// latest text replaces the previous one rather than appending.
|
|
272
|
+
if (event.status === 'completed' || event.status === 'failed') {
|
|
273
|
+
this.toolCallArgsByCallId.delete(event.toolCallId);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const argsText = extractToolCallContentText(event.content);
|
|
277
|
+
if (argsText !== undefined) {
|
|
278
|
+
this.trackToolCallArgs(event.toolCallId, argsText);
|
|
279
|
+
}
|
|
280
|
+
else if (event.rawInput !== undefined && event.rawInput !== null) {
|
|
281
|
+
this.trackToolCallArgs(event.toolCallId, stringifyUnknown(event.rawInput));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
221
284
|
return [
|
|
222
285
|
{
|
|
223
286
|
type: 'user',
|
|
@@ -235,6 +298,25 @@ export class BaseAcpTranslator {
|
|
|
235
298
|
},
|
|
236
299
|
];
|
|
237
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Latest tracked streaming-args text for a tool call (undefined when never
|
|
303
|
+
* seen or already terminated). Approval handlers correlate
|
|
304
|
+
* request_permission's toolCall.toolCallId against this to recover the real
|
|
305
|
+
* command (see toolCallArgsByCallId).
|
|
306
|
+
*/
|
|
307
|
+
getTrackedToolCallArgs(toolCallId) {
|
|
308
|
+
return this.toolCallArgsByCallId.get(toolCallId);
|
|
309
|
+
}
|
|
310
|
+
/** Record latest args text, bounded so a long session cannot grow it forever. */
|
|
311
|
+
trackToolCallArgs(toolCallId, argsText) {
|
|
312
|
+
this.toolCallArgsByCallId.delete(toolCallId);
|
|
313
|
+
this.toolCallArgsByCallId.set(toolCallId, argsText);
|
|
314
|
+
if (this.toolCallArgsByCallId.size > 100) {
|
|
315
|
+
const oldest = this.toolCallArgsByCallId.keys().next().value;
|
|
316
|
+
if (oldest !== undefined)
|
|
317
|
+
this.toolCallArgsByCallId.delete(oldest);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
238
320
|
/**
|
|
239
321
|
* tool_result content extraction. Default: rawOutput passthrough (kimi
|
|
240
322
|
* semantics). opencode overrides — its rawOutput is an OBJECT
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* 以 `turn_diff` 快照发出——禁止把 delta 直接当快照或把全文当增量重复追加。
|
|
23
23
|
* 4. `tool_call` 事件可能**没有 rawInput**(工具 lazy-create,参数在后续
|
|
24
24
|
* tool_call_update 才补齐);translator 必须归一化为 `{}`,不得因缺失崩溃。
|
|
25
|
+
* 5. `session/request_permission` 的 toolCall **不一定带 rawInput**(kimi 实测:
|
|
26
|
+
* 审批门先于 tool.call.started,只有 title + content 截断摘要);完整命令
|
|
27
|
+
* 需按 toolCallId 关联审批前的流式 args 文本恢复(BaseAcpTranslator 追踪)。
|
|
25
28
|
*/
|
|
26
29
|
export type { JsonRpcRequest, JsonRpcNotification, JsonRpcSuccessResponse, JsonRpcErrorResponse, } from '../jsonrpc/types.js';
|
|
27
30
|
/** Standard JSON-RPC error codes. */
|
|
@@ -226,9 +229,20 @@ export interface PermissionOption {
|
|
|
226
229
|
}
|
|
227
230
|
export interface RequestPermissionParams {
|
|
228
231
|
sessionId: string;
|
|
232
|
+
/**
|
|
233
|
+
* Approval target. Divergence by server (2026-09-13 live probes):
|
|
234
|
+
* - kimi 0.42: {toolCallId, title: toolName, content: [truncated summary
|
|
235
|
+
* text]} — NO rawInput; the approval gate fires BEFORE tool.call.started,
|
|
236
|
+
* so the full args only exist in the pre-approval tool_call/_update
|
|
237
|
+
* streaming deltas (correlate by toolCallId, see BaseAcpTranslator).
|
|
238
|
+
* - opencode: {toolCallId, title: full command for bash/shell, rawInput:
|
|
239
|
+
* full input object} — title/rawInput are directly usable.
|
|
240
|
+
*/
|
|
229
241
|
toolCall?: {
|
|
230
|
-
|
|
231
|
-
|
|
242
|
+
toolCallId?: string;
|
|
243
|
+
title?: string;
|
|
244
|
+
rawInput?: unknown;
|
|
245
|
+
content?: unknown[];
|
|
232
246
|
[key: string]: unknown;
|
|
233
247
|
};
|
|
234
248
|
options: PermissionOption[];
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* 以 `turn_diff` 快照发出——禁止把 delta 直接当快照或把全文当增量重复追加。
|
|
23
23
|
* 4. `tool_call` 事件可能**没有 rawInput**(工具 lazy-create,参数在后续
|
|
24
24
|
* tool_call_update 才补齐);translator 必须归一化为 `{}`,不得因缺失崩溃。
|
|
25
|
+
* 5. `session/request_permission` 的 toolCall **不一定带 rawInput**(kimi 实测:
|
|
26
|
+
* 审批门先于 tool.call.started,只有 title + content 截断摘要);完整命令
|
|
27
|
+
* 需按 toolCallId 关联审批前的流式 args 文本恢复(BaseAcpTranslator 追踪)。
|
|
25
28
|
*/
|
|
26
29
|
/** Standard JSON-RPC error codes. */
|
|
27
30
|
export const RpcErrorCode = {
|
|
@@ -120,13 +120,31 @@ export class KimiAcpTranslator extends BaseAcpTranslator {
|
|
|
120
120
|
getLogger().info(`[kimi-acp-translator] permission request without toolCall, auto-responding cancelled (requestId=${requestId})`);
|
|
121
121
|
return [];
|
|
122
122
|
}
|
|
123
|
+
// 命令恢复(2026-09-13 对 kimi 0.42.0 实测):审批门在 tool.call.started
|
|
124
|
+
// 之前触发,request_permission 的 toolCall 只有 title("Bash")+ content
|
|
125
|
+
// 里一句截断摘要("Requesting approval to Running: <cmd>…"),无 rawInput;
|
|
126
|
+
// 完整命令只从审批前的 tool_call/tool_call_update 流式 args 累积文本拿到
|
|
127
|
+
// (基类按 toolCallId 追踪)。解析顺序:流式 args JSON 的 command 字段 →
|
|
128
|
+
// title;流式缺失时退回 content 摘要放进 reason(旧版 kimi 不推 delta)。
|
|
129
|
+
const tracked = typeof toolCall.toolCallId === 'string'
|
|
130
|
+
? this.getTrackedToolCallArgs(toolCall.toolCallId)
|
|
131
|
+
: undefined;
|
|
132
|
+
const argsCommand = tracked ? extractCommandFromArgsText(tracked) : undefined;
|
|
133
|
+
const rawInput = typeof toolCall.rawInput === 'string' && toolCall.rawInput.length > 0
|
|
134
|
+
? toolCall.rawInput
|
|
135
|
+
: undefined;
|
|
136
|
+
const summary = extractPermissionSummaryText(toolCall.content);
|
|
123
137
|
const view = {
|
|
124
138
|
requestId,
|
|
125
139
|
kind: 'command',
|
|
126
|
-
command:
|
|
127
|
-
reason:
|
|
128
|
-
?
|
|
129
|
-
:
|
|
140
|
+
command: argsCommand ?? toolCall.title ?? undefined,
|
|
141
|
+
reason: argsCommand
|
|
142
|
+
? undefined
|
|
143
|
+
: rawInput
|
|
144
|
+
? truncateWithEllipsis(rawInput, 200)
|
|
145
|
+
: summary
|
|
146
|
+
? truncateWithEllipsis(summary, 200)
|
|
147
|
+
: undefined,
|
|
130
148
|
// §P4: 从服务端 options kind 派生——带 approve_always(或 allow_always)
|
|
131
149
|
// 才提供「本会话总是允许」(acceptForSession);否则与旧行为一致。
|
|
132
150
|
availableDecisions: deriveAcpAvailableDecisions(params.options ?? []),
|
|
@@ -244,4 +262,42 @@ function extractQuestionText(toolCall) {
|
|
|
244
262
|
}
|
|
245
263
|
return toolCall?.title?.trim() || '请选择';
|
|
246
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* 从流式累积的 args 文本恢复命令:完整 JSON 且含 string command 字段时取该
|
|
267
|
+
* 字段;否则(非 Bash 工具 / 防御性半截 JSON)返回原文。
|
|
268
|
+
*/
|
|
269
|
+
function extractCommandFromArgsText(argsText) {
|
|
270
|
+
try {
|
|
271
|
+
const parsed = JSON.parse(argsText);
|
|
272
|
+
if (parsed && typeof parsed === 'object') {
|
|
273
|
+
const command = parsed.command;
|
|
274
|
+
if (typeof command === 'string' && command.length > 0)
|
|
275
|
+
return command;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
// 半截 JSON(审批正常在 args 流式完成后才到,此处纯防御)——用原文。
|
|
280
|
+
}
|
|
281
|
+
return argsText;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* request_permission toolCall.content 里的摘要文本(kimi 0.42:
|
|
285
|
+
* "Requesting approval to Running: <cmd>…",约 64 字符截断)。无文本返回
|
|
286
|
+
* undefined。
|
|
287
|
+
*/
|
|
288
|
+
function extractPermissionSummaryText(content) {
|
|
289
|
+
if (!Array.isArray(content))
|
|
290
|
+
return undefined;
|
|
291
|
+
let text = '';
|
|
292
|
+
let found = false;
|
|
293
|
+
for (const block of content) {
|
|
294
|
+
const t = block?.content
|
|
295
|
+
?.text;
|
|
296
|
+
if (typeof t === 'string' && t.length > 0) {
|
|
297
|
+
text += t;
|
|
298
|
+
found = true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return found ? text : undefined;
|
|
302
|
+
}
|
|
247
303
|
//# sourceMappingURL=translator.js.map
|
|
@@ -14,7 +14,7 @@ interface SessionEntry {
|
|
|
14
14
|
/**
|
|
15
15
|
* Per-user session store. Persists cwd + per-agent sessions to disk so that
|
|
16
16
|
* the last workspace AND the last-used sessionId per agent are restored after
|
|
17
|
-
* a
|
|
17
|
+
* a lark-remote restart.
|
|
18
18
|
*
|
|
19
19
|
* Persistence strategy:
|
|
20
20
|
* - Persists `cwd`, `sessions` (per-agent sessionId), `previousSessions`
|
|
@@ -4,7 +4,7 @@ import { atomicWriteJson } from '../persistence/atomic-write.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Per-user session store. Persists cwd + per-agent sessions to disk so that
|
|
6
6
|
* the last workspace AND the last-used sessionId per agent are restored after
|
|
7
|
-
* a
|
|
7
|
+
* a lark-remote restart.
|
|
8
8
|
*
|
|
9
9
|
* Persistence strategy:
|
|
10
10
|
* - Persists `cwd`, `sessions` (per-agent sessionId), `previousSessions`
|
|
@@ -9,7 +9,7 @@ export interface WorkspaceEntry {
|
|
|
9
9
|
*
|
|
10
10
|
* Atomic write (§9.10): writes a temp file then `fs.rename` so a crash
|
|
11
11
|
* mid-write cannot leave a truncated JSON file. On load, a corrupt file
|
|
12
|
-
* is treated as empty (with a warning) so the
|
|
12
|
+
* is treated as empty (with a warning) so the lark-remote can still start.
|
|
13
13
|
*
|
|
14
14
|
* lastUsedAt 语义(2026-08-19 语义对齐):保存(save)与使用(use/touch)
|
|
15
15
|
* 都会把时间戳更新为当前时间。save 内部调用 touch,保证「最近使用」排序
|
package/dist/workspace/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { getLogger } from '../logger/index.js';
|
|
|
8
8
|
*
|
|
9
9
|
* Atomic write (§9.10): writes a temp file then `fs.rename` so a crash
|
|
10
10
|
* mid-write cannot leave a truncated JSON file. On load, a corrupt file
|
|
11
|
-
* is treated as empty (with a warning) so the
|
|
11
|
+
* is treated as empty (with a warning) so the lark-remote can still start.
|
|
12
12
|
*
|
|
13
13
|
* lastUsedAt 语义(2026-08-19 语义对齐):保存(save)与使用(use/touch)
|
|
14
14
|
* 都会把时间戳更新为当前时间。save 内部调用 touch,保证「最近使用」排序
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"test:opencode-full": "vitest run --project agent-opencode --project infra-runner --project infra-session",
|
|
59
59
|
"test:pi-full": "vitest run --project agent-pi --project infra-runner --project infra-session",
|
|
60
60
|
"test:kimi-full": "vitest run --project agent-kimi --project infra-runner --project infra-session",
|
|
61
|
-
"test:live": "vitest run --project live",
|
|
61
|
+
"test:live": "node node_modules/vitest/vitest.mjs run --project live",
|
|
62
62
|
"prepublishOnly": "npm run typecheck && vitest run && npm run build",
|
|
63
63
|
"prepack": "node -e \"const fs=require('fs');if(!fs.existsSync('dist/cli.js')){console.error('dist/ missing — run bun run build first');process.exit(1)}\""
|
|
64
64
|
},
|