lark-remote 0.1.13 → 0.2.1
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 +36 -3
- package/README.en.md +7 -11
- package/README.md +7 -11
- package/dist/bridge/final-usage.d.ts +24 -0
- package/dist/bridge/final-usage.js +55 -0
- package/dist/bridge/index.d.ts +0 -7
- package/dist/bridge/index.js +22 -64
- package/dist/bridge/queue-manager.js +4 -3
- package/dist/card/approval-render.js +39 -94
- package/dist/card/bash-renderer.js +12 -21
- package/dist/card/card-shared.d.ts +13 -1
- package/dist/card/card-shared.js +21 -30
- package/dist/card/run-renderer.d.ts +0 -22
- package/dist/card/run-renderer.js +206 -153
- package/dist/card/run-state.d.ts +29 -1
- package/dist/card/run-state.js +172 -84
- package/dist/card/text-truncate.d.ts +0 -9
- package/dist/card/text-truncate.js +0 -9
- package/dist/card/tool-render.d.ts +4 -1
- package/dist/card/tool-render.js +76 -32
- package/dist/cli.js +2 -2
- package/dist/common/fs.d.ts +14 -0
- package/dist/common/fs.js +57 -3
- package/dist/config/codex-config.js +2 -0
- package/dist/config/index.d.ts +0 -5
- package/dist/config/index.js +0 -11
- package/dist/config/kimi-config.js +1 -0
- package/dist/config/opencode-config.js +13 -2
- package/dist/config/qr.d.ts +36 -0
- package/dist/config/qr.js +124 -0
- package/dist/config/wizard.js +69 -27
- package/dist/connector/index.d.ts +3 -2
- package/dist/connector/index.js +5 -4
- package/dist/index.js +15 -42
- package/dist/instance-lock.js +7 -0
- package/dist/persistence/atomic-write.d.ts +2 -0
- package/dist/persistence/atomic-write.js +20 -5
- package/dist/persistence/load-json-file.d.ts +1 -2
- package/dist/persistence/load-json-file.js +2 -3
- package/dist/platform/agent-stopper.d.ts +22 -0
- package/dist/platform/agent-stopper.js +19 -0
- package/dist/platform/command.d.ts +11 -0
- package/dist/platform/command.js +107 -0
- package/dist/platform/identity.d.ts +33 -0
- package/dist/platform/identity.js +187 -0
- package/dist/platform/path.d.ts +24 -0
- package/dist/platform/path.js +110 -0
- package/dist/platform/select.d.ts +9 -0
- package/dist/platform/select.js +12 -0
- package/dist/platform/shell.d.ts +51 -0
- package/dist/platform/shell.js +75 -0
- package/dist/platform/spawn.d.ts +41 -0
- package/dist/platform/spawn.js +77 -0
- package/dist/platform/terminator-posix.d.ts +38 -0
- package/dist/platform/terminator-posix.js +103 -0
- package/dist/platform/terminator-win32.d.ts +21 -0
- package/dist/platform/terminator-win32.js +110 -0
- package/dist/platform/terminator.d.ts +44 -0
- package/dist/platform/terminator.js +24 -0
- package/dist/platform/types.d.ts +23 -0
- package/dist/platform/types.js +5 -0
- package/dist/restart.js +4 -2
- package/dist/router/card-helpers.d.ts +33 -6
- package/dist/router/card-helpers.js +67 -11
- package/dist/router/config/common/render.d.ts +1 -1
- package/dist/router/config/common/render.js +1 -1
- package/dist/router/config-diff.d.ts +4 -0
- package/dist/router/config-diff.js +70 -0
- package/dist/router/index.d.ts +66 -6
- package/dist/router/index.js +451 -316
- package/dist/router/utils.d.ts +6 -0
- package/dist/router/utils.js +12 -0
- package/dist/runner/bash/runner.d.ts +9 -2
- package/dist/runner/bash/runner.js +45 -20
- package/dist/runner/claude/session.d.ts +4 -1
- package/dist/runner/claude/session.js +18 -4
- package/dist/runner/codex/app-server/protocol-types.d.ts +35 -1
- package/dist/runner/codex/app-server/translator.d.ts +18 -0
- package/dist/runner/codex/app-server/translator.js +146 -0
- package/dist/runner/common/acp/base-acp-runner.d.ts +116 -0
- package/dist/runner/common/acp/base-acp-runner.js +248 -0
- package/dist/runner/common/acp/base-translator.d.ts +141 -0
- package/dist/runner/common/acp/base-translator.js +271 -0
- package/dist/runner/common/acp/protocol-helpers.d.ts +39 -0
- package/dist/runner/common/acp/protocol-helpers.js +34 -0
- package/dist/runner/common/jsonrpc/transport.d.ts +2 -0
- package/dist/runner/common/jsonrpc/transport.js +25 -5
- package/dist/runner/common/process-stopper.d.ts +4 -26
- package/dist/runner/common/process-stopper.js +4 -76
- package/dist/runner/common/runner-utils.d.ts +0 -5
- package/dist/runner/common/runner-utils.js +0 -7
- package/dist/runner/common/spawning-runner.d.ts +42 -132
- package/dist/runner/common/spawning-runner.js +52 -271
- package/dist/runner/common/usage.d.ts +2 -0
- package/dist/runner/common/usage.js +2 -0
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +45 -42
- package/dist/runner/kimi/acp/runner.js +140 -293
- package/dist/runner/kimi/acp/translator.d.ts +18 -100
- package/dist/runner/kimi/acp/translator.js +56 -242
- package/dist/runner/opencode/acp/runner.d.ts +22 -46
- package/dist/runner/opencode/acp/runner.js +73 -256
- package/dist/runner/opencode/acp/translator.d.ts +28 -103
- package/dist/runner/opencode/acp/translator.js +51 -236
- package/dist/runner/pi/rpc/translator.d.ts +3 -0
- package/dist/runner/pi/rpc/translator.js +47 -9
- package/dist/runner/probe.d.ts +8 -11
- package/dist/runner/probe.js +17 -21
- package/dist/runner/registry.d.ts +4 -4
- package/dist/runner/registry.js +4 -4
- package/dist/runner/types.d.ts +38 -1
- package/dist/session/claude/sessions.js +35 -58
- package/dist/session/codex/rollout-reader.d.ts +10 -0
- package/dist/session/codex/rollout-reader.js +123 -41
- package/dist/session/common/constants.d.ts +7 -0
- package/dist/session/common/constants.js +9 -0
- package/dist/session/common/two-pass.d.ts +22 -0
- package/dist/session/common/two-pass.js +70 -0
- package/dist/session/dsh/sessions.js +7 -6
- package/dist/session/kimi/sessions.d.ts +6 -0
- package/dist/session/kimi/sessions.js +50 -73
- package/dist/session/opencode/sessions.js +26 -8
- package/dist/session/pi/sessions.js +54 -73
- package/dist/update/index.d.ts +1 -2
- package/dist/update/index.js +1 -2
- package/dist/update/install.d.ts +2 -1
- package/dist/update/install.js +58 -9
- package/dist/update/version-check.d.ts +5 -0
- package/dist/update/version-check.js +9 -0
- package/package.json +9 -6
- package/dist/update/startup-hint.d.ts +0 -5
- package/dist/update/startup-hint.js +0 -11
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* KimiAcpTranslator: translates ACP session/update notifications and
|
|
3
3
|
* session/request_permission server requests into AgentEvents.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Extends BaseAcpTranslator (shared envelope dispatch, prompt-response
|
|
6
|
+
* stopReason mapping, tool_call normalization, usage occupancy). Kimi's
|
|
7
|
+
* divergence from opencode is the content channel: per-turn stateful —
|
|
8
|
+
* accumulates text/thinking deltas and emits turn_diff snapshots (NOT
|
|
9
|
+
* assistant deltas — see handleAgentMessageChunk), plus question elicitation.
|
|
7
10
|
*
|
|
8
11
|
* Wire envelope (R1, source: kimi-code packages/acp-server/src/events-map.ts,
|
|
9
12
|
* verified live 2026-08-15/16 against kimi 0.36.0):
|
|
@@ -13,74 +16,28 @@
|
|
|
13
16
|
* Event mapping (design doc §4.2):
|
|
14
17
|
* agent_message_chunk → turn_diff text snapshot (content.text accumulation)
|
|
15
18
|
* agent_thought_chunk → turn_diff reasoning snapshot (content.text accumulation)
|
|
16
|
-
* tool_call
|
|
17
|
-
*
|
|
18
|
-
* tool_call_update → user/tool_result (status:'failed' → is_error:true)
|
|
19
|
-
* plan / available_commands_update / session_info_update /
|
|
20
|
-
* current_mode_update / config_option_update → discard
|
|
21
|
-
* usage_update → live context occupancy {used, size} →
|
|
22
|
-
* {total_tokens, context_limit}; NO input/output
|
|
23
|
-
* split exists on the wire — cumulative token stats
|
|
24
|
-
* fall back to wire.jsonl usage.record (dual path)
|
|
19
|
+
* tool_call / tool_call_update / usage_update / plan / control-plane noise →
|
|
20
|
+
* base class (BaseAcpTranslator)
|
|
25
21
|
* prompt stopReason:'end_turn' → result success
|
|
26
22
|
* prompt stopReason:'cancelled' → result interrupted (独立终态)
|
|
27
23
|
* prompt other stopReason/error → result error
|
|
28
24
|
* turn_started (self-produced) → operationKind 'turn'/'compaction'
|
|
29
|
-
* session/request_permission → approval_requested
|
|
25
|
+
* session/request_permission → approval_requested(含 question 桥)
|
|
26
|
+
* elicitation/create → question approval event
|
|
30
27
|
*/
|
|
31
|
-
import type
|
|
32
|
-
import type
|
|
33
|
-
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
threadId: string;
|
|
37
|
-
turnId: string;
|
|
38
|
-
operationKind: 'turn' | 'compaction';
|
|
39
|
-
timestamp?: string;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Live usage snapshot from usage_update. The wire only carries context
|
|
43
|
-
* occupancy (`used`) and the model context window (`size`) — there is NO
|
|
44
|
-
* input/output token split (events-map.ts:505-516). input_tokens/output_tokens
|
|
45
|
-
* stay undefined so the bridge falls back to wire.jsonl usage.record for
|
|
46
|
-
* cumulative token stats (R1 dual path).
|
|
47
|
-
*/
|
|
48
|
-
export interface AcpLiveUsage {
|
|
49
|
-
total_tokens?: number;
|
|
50
|
-
context_limit?: number;
|
|
51
|
-
input_tokens?: number;
|
|
52
|
-
output_tokens?: number;
|
|
53
|
-
}
|
|
54
|
-
/** Usage update event for live token display. */
|
|
55
|
-
export interface AcpUsageEvent {
|
|
56
|
-
type: 'usage';
|
|
57
|
-
usage: AcpLiveUsage;
|
|
58
|
-
timestamp?: string;
|
|
59
|
-
}
|
|
60
|
-
export type AcpTranslatorEvent = AgentEvent | AcpTurnStartedEvent | AcpUsageEvent | ApprovalRequestedEvent;
|
|
61
|
-
export declare class KimiAcpTranslator {
|
|
28
|
+
import { type RequestPermissionParams, type AgentMessageChunkEvent, type AgentThoughtChunkEvent } from '../../common/acp/protocol-types.js';
|
|
29
|
+
import { BaseAcpTranslator, type AcpTranslatorEvent } from '../../common/acp/base-translator.js';
|
|
30
|
+
export type { AcpTurnStartedEvent, AcpLiveUsage, AcpUsageEvent, AcpTranslatorEvent, } from '../../common/acp/base-translator.js';
|
|
31
|
+
export declare class KimiAcpTranslator extends BaseAcpTranslator {
|
|
32
|
+
protected readonly logTag = "kimi-acp-translator";
|
|
62
33
|
/** Accumulated text per tool-call or message (keyed by a logical item id). */
|
|
63
34
|
private textByItem;
|
|
64
35
|
/** Accumulated thinking per item. */
|
|
65
36
|
private thinkingByItem;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
/** Live usage snapshot from usage_update events (context occupancy only). */
|
|
71
|
-
private liveUsage;
|
|
72
|
-
/** Whether any usage_update has been seen this turn (no update → no usage). */
|
|
73
|
-
private hasLiveUsage;
|
|
74
|
-
/** Current turn id, set by produceTurnStarted; carried on turn_diff events. */
|
|
75
|
-
private currentTurnId;
|
|
76
|
-
/**
|
|
77
|
-
* Handle a notification from the ACP server and return translated events.
|
|
78
|
-
*/
|
|
79
|
-
handleNotification(method: string, params: unknown): AcpTranslatorEvent[];
|
|
80
|
-
/**
|
|
81
|
-
* Handle a server request (reverse RPC from the ACP server).
|
|
82
|
-
*/
|
|
83
|
-
handleServerRequest(id: number | string, method: string, params: unknown): AcpTranslatorEvent[];
|
|
37
|
+
protected handleAgentMessageChunk(event: AgentMessageChunkEvent, sessionId: string): AcpTranslatorEvent[];
|
|
38
|
+
protected handleAgentThoughtChunk(event: AgentThoughtChunkEvent, sessionId: string): AcpTranslatorEvent[];
|
|
39
|
+
protected handleRequestPermission(requestId: number | string, params: RequestPermissionParams): AcpTranslatorEvent[];
|
|
40
|
+
protected handleOtherServerRequest(requestId: number | string, method: string, params: unknown): AcpTranslatorEvent[];
|
|
84
41
|
/**
|
|
85
42
|
* Kimi elicitation form:requestedSchema.properties 按顺序解析为 UserQuestion[]
|
|
86
43
|
* (type:array → 多选;oneOf/anyOf const → 选项 label;title → header??question)。
|
|
@@ -89,49 +46,10 @@ export declare class KimiAcpTranslator {
|
|
|
89
46
|
* form 会丢弃非声明选项值 → isOther=false(卡片隐藏自定义答案输入)。
|
|
90
47
|
*/
|
|
91
48
|
private handleElicitationCreate;
|
|
92
|
-
/**
|
|
93
|
-
* Produce a turn_started event. Called by the runner when session/prompt
|
|
94
|
-
* is sent (no wire notification for this — self-produced).
|
|
95
|
-
*/
|
|
96
|
-
produceTurnStarted(sessionId: string, turnId: string): AcpTurnStartedEvent;
|
|
97
|
-
/**
|
|
98
|
-
* Translate a prompt response into a result event. Carries the live usage
|
|
99
|
-
* snapshot only when a usage_update was seen (otherwise undefined, so the
|
|
100
|
-
* bridge falls back to wire.jsonl usage.record readback).
|
|
101
|
-
*/
|
|
102
|
-
handlePromptResponse(sessionId: string, result: {
|
|
103
|
-
stopReason: string;
|
|
104
|
-
}): AgentEvent;
|
|
105
|
-
/**
|
|
106
|
-
* Produce an error result event (for JSON-RPC errors during prompt).
|
|
107
|
-
*/
|
|
108
|
-
produceErrorResult(sessionId: string, errorMessage: string): AgentEvent;
|
|
109
|
-
/**
|
|
110
|
-
* Set the operation kind (turn vs compact).
|
|
111
|
-
*/
|
|
112
|
-
setOperationKind(kind: 'turn' | 'compact'): void;
|
|
113
|
-
private handleAgentMessageChunk;
|
|
114
|
-
private handleAgentThoughtChunk;
|
|
115
|
-
private handleToolCall;
|
|
116
|
-
private handleToolCallUpdate;
|
|
117
|
-
private handleUsageUpdate;
|
|
118
|
-
private handleRequestPermission;
|
|
119
49
|
/**
|
|
120
50
|
* request_permission 兜底桥(elicitation/create 失败或旧版 kimi):
|
|
121
51
|
* 选项为 q{n}_opt_{i}(allow_once,label 直取 name)+ q{n}_skip(reject_once,
|
|
122
52
|
* 跳过语义)。单题单选、无 Other(kimi 桥丢弃非声明值)。
|
|
123
53
|
*/
|
|
124
54
|
private handlePermissionQuestion;
|
|
125
|
-
/**
|
|
126
|
-
* Derive the approval decision list from the server's offered options.
|
|
127
|
-
* accept/decline/cancel are universal; acceptForSession requires an
|
|
128
|
-
* always-class option (kimi `approve_always`, opencode `allow_always`).
|
|
129
|
-
*/
|
|
130
|
-
/**
|
|
131
|
-
* ResultEvent.usage requires input_tokens/output_tokens: number, but the
|
|
132
|
-
* ACP wire has no such split — the live snapshot is context occupancy only.
|
|
133
|
-
* Cast is deliberate: consumers must tolerate undefined input/output (the
|
|
134
|
-
* bridge's kimi path already falls back to wire.jsonl readback).
|
|
135
|
-
*/
|
|
136
|
-
private liveUsageSnapshot;
|
|
137
55
|
}
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* KimiAcpTranslator: translates ACP session/update notifications and
|
|
3
3
|
* session/request_permission server requests into AgentEvents.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Extends BaseAcpTranslator (shared envelope dispatch, prompt-response
|
|
6
|
+
* stopReason mapping, tool_call normalization, usage occupancy). Kimi's
|
|
7
|
+
* divergence from opencode is the content channel: per-turn stateful —
|
|
8
|
+
* accumulates text/thinking deltas and emits turn_diff snapshots (NOT
|
|
9
|
+
* assistant deltas — see handleAgentMessageChunk), plus question elicitation.
|
|
7
10
|
*
|
|
8
11
|
* Wire envelope (R1, source: kimi-code packages/acp-server/src/events-map.ts,
|
|
9
12
|
* verified live 2026-08-15/16 against kimi 0.36.0):
|
|
@@ -13,25 +16,20 @@
|
|
|
13
16
|
* Event mapping (design doc §4.2):
|
|
14
17
|
* agent_message_chunk → turn_diff text snapshot (content.text accumulation)
|
|
15
18
|
* agent_thought_chunk → turn_diff reasoning snapshot (content.text accumulation)
|
|
16
|
-
* tool_call
|
|
17
|
-
*
|
|
18
|
-
* tool_call_update → user/tool_result (status:'failed' → is_error:true)
|
|
19
|
-
* plan / available_commands_update / session_info_update /
|
|
20
|
-
* current_mode_update / config_option_update → discard
|
|
21
|
-
* usage_update → live context occupancy {used, size} →
|
|
22
|
-
* {total_tokens, context_limit}; NO input/output
|
|
23
|
-
* split exists on the wire — cumulative token stats
|
|
24
|
-
* fall back to wire.jsonl usage.record (dual path)
|
|
19
|
+
* tool_call / tool_call_update / usage_update / plan / control-plane noise →
|
|
20
|
+
* base class (BaseAcpTranslator)
|
|
25
21
|
* prompt stopReason:'end_turn' → result success
|
|
26
22
|
* prompt stopReason:'cancelled' → result interrupted (独立终态)
|
|
27
23
|
* prompt other stopReason/error → result error
|
|
28
24
|
* turn_started (self-produced) → operationKind 'turn'/'compaction'
|
|
29
|
-
* session/request_permission → approval_requested
|
|
25
|
+
* session/request_permission → approval_requested(含 question 桥)
|
|
26
|
+
* elicitation/create → question approval event
|
|
30
27
|
*/
|
|
31
28
|
import { makeQuestionApprovalEvent } from '../../question-common.js';
|
|
32
|
-
import {
|
|
29
|
+
import { ServerRequestMethod, } from '../../common/acp/protocol-types.js';
|
|
33
30
|
import { deriveAcpAvailableDecisions, truncateWithEllipsis, } from '../../common/acp/protocol-helpers.js';
|
|
34
31
|
import { getLogger } from '../../../logger/index.js';
|
|
32
|
+
import { BaseAcpTranslator } from '../../common/acp/base-translator.js';
|
|
35
33
|
/**
|
|
36
34
|
* Fixed snapshot item ids: ACP has exactly one text stream and one thinking
|
|
37
35
|
* stream per turn (no itemId on the wire), so turn_diff snapshots use these
|
|
@@ -42,153 +40,14 @@ const THINKING_ITEM_ID = 'thinking';
|
|
|
42
40
|
// =============================================================================
|
|
43
41
|
// Translator
|
|
44
42
|
// =============================================================================
|
|
45
|
-
export class KimiAcpTranslator {
|
|
43
|
+
export class KimiAcpTranslator extends BaseAcpTranslator {
|
|
44
|
+
logTag = 'kimi-acp-translator';
|
|
46
45
|
/** Accumulated text per tool-call or message (keyed by a logical item id). */
|
|
47
46
|
textByItem = new Map();
|
|
48
47
|
/** Accumulated thinking per item. */
|
|
49
48
|
thinkingByItem = new Map();
|
|
50
|
-
/** Pending tool calls keyed by toolCallId (for matching tool_call_update). */
|
|
51
|
-
pendingToolCalls = new Map();
|
|
52
|
-
/** Current operation kind (turn vs compact). */
|
|
53
|
-
operationKind = 'turn';
|
|
54
|
-
/** Live usage snapshot from usage_update events (context occupancy only). */
|
|
55
|
-
liveUsage = {};
|
|
56
|
-
/** Whether any usage_update has been seen this turn (no update → no usage). */
|
|
57
|
-
hasLiveUsage = false;
|
|
58
|
-
/** Current turn id, set by produceTurnStarted; carried on turn_diff events. */
|
|
59
|
-
currentTurnId = '';
|
|
60
|
-
/**
|
|
61
|
-
* Handle a notification from the ACP server and return translated events.
|
|
62
|
-
*/
|
|
63
|
-
handleNotification(method, params) {
|
|
64
|
-
if (method !== NotificationMethod.SESSION_UPDATE) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
// R1: real envelope is {sessionId, update: {sessionUpdate: ...}}.
|
|
68
|
-
const notif = params;
|
|
69
|
-
const update = notif.update;
|
|
70
|
-
switch (update.sessionUpdate) {
|
|
71
|
-
case SessionEventType.AGENT_MESSAGE_CHUNK:
|
|
72
|
-
return this.handleAgentMessageChunk(update, notif.sessionId);
|
|
73
|
-
case SessionEventType.AGENT_THOUGHT_CHUNK:
|
|
74
|
-
return this.handleAgentThoughtChunk(update, notif.sessionId);
|
|
75
|
-
case SessionEventType.TOOL_CALL:
|
|
76
|
-
return this.handleToolCall(update, notif.sessionId);
|
|
77
|
-
case SessionEventType.TOOL_CALL_UPDATE:
|
|
78
|
-
return this.handleToolCallUpdate(update, notif.sessionId);
|
|
79
|
-
case SessionEventType.PLAN:
|
|
80
|
-
getLogger().debug(`[kimi-acp-translator] discarding plan event`);
|
|
81
|
-
return [];
|
|
82
|
-
case SessionEventType.AVAILABLE_COMMANDS_UPDATE:
|
|
83
|
-
case SessionEventType.SESSION_INFO_UPDATE:
|
|
84
|
-
case SessionEventType.CURRENT_MODE_UPDATE:
|
|
85
|
-
case SessionEventType.CONFIG_OPTION_UPDATE:
|
|
86
|
-
// Control-plane noise (command list, mode/config echoes) — not content.
|
|
87
|
-
return [];
|
|
88
|
-
case SessionEventType.USAGE_UPDATE:
|
|
89
|
-
return this.handleUsageUpdate(update);
|
|
90
|
-
default:
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Handle a server request (reverse RPC from the ACP server).
|
|
96
|
-
*/
|
|
97
|
-
handleServerRequest(id, method, params) {
|
|
98
|
-
if (method === ServerRequestMethod.REQUEST_PERMISSION) {
|
|
99
|
-
return this.handleRequestPermission(id, params);
|
|
100
|
-
}
|
|
101
|
-
if (method === ServerRequestMethod.ELICITATION_CREATE) {
|
|
102
|
-
return this.handleElicitationCreate(id, params);
|
|
103
|
-
}
|
|
104
|
-
return [];
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Kimi elicitation form:requestedSchema.properties 按顺序解析为 UserQuestion[]
|
|
108
|
-
* (type:array → 多选;oneOf/anyOf const → 选项 label;title → header??question)。
|
|
109
|
-
* 题面完整文本只在 form 级 message 里合并出现(逐字段 title 可能只是短 header),
|
|
110
|
-
* 因此 message 作为卡片概要行(view.intro)透传。
|
|
111
|
-
* form 会丢弃非声明选项值 → isOther=false(卡片隐藏自定义答案输入)。
|
|
112
|
-
*/
|
|
113
|
-
handleElicitationCreate(requestId, params) {
|
|
114
|
-
const properties = params.requestedSchema?.properties ?? {};
|
|
115
|
-
const questions = [];
|
|
116
|
-
for (const [key, prop] of Object.entries(properties)) {
|
|
117
|
-
const q = elicitationPropertyToQuestion(prop);
|
|
118
|
-
if (!q) {
|
|
119
|
-
getLogger().warn(`[kimi-acp-translator] elicitation property skipped: key=${key}`);
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
questions.push(q);
|
|
123
|
-
}
|
|
124
|
-
if (questions.length === 0) {
|
|
125
|
-
// 无法解析 → 交还 runner 自动响应(不悬挂服务端)。
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
const message = params.message?.trim();
|
|
129
|
-
const single = questions.length === 1;
|
|
130
|
-
const intro = message && (!single || message !== questions[0].question) ? message : undefined;
|
|
131
|
-
const event = makeQuestionApprovalEvent(requestId, questions, params.sessionId);
|
|
132
|
-
if (intro)
|
|
133
|
-
event.view.intro = intro;
|
|
134
|
-
return [event];
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Produce a turn_started event. Called by the runner when session/prompt
|
|
138
|
-
* is sent (no wire notification for this — self-produced).
|
|
139
|
-
*/
|
|
140
|
-
produceTurnStarted(sessionId, turnId) {
|
|
141
|
-
this.currentTurnId = turnId;
|
|
142
|
-
return {
|
|
143
|
-
type: 'turn_started',
|
|
144
|
-
threadId: sessionId,
|
|
145
|
-
turnId,
|
|
146
|
-
operationKind: this.operationKind === 'compact' ? 'compaction' : 'turn',
|
|
147
|
-
timestamp: new Date().toISOString(),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Translate a prompt response into a result event. Carries the live usage
|
|
152
|
-
* snapshot only when a usage_update was seen (otherwise undefined, so the
|
|
153
|
-
* bridge falls back to wire.jsonl usage.record readback).
|
|
154
|
-
*/
|
|
155
|
-
handlePromptResponse(sessionId, result) {
|
|
156
|
-
// §4.2: cancelled is independent terminal state, must NOT merge into error
|
|
157
|
-
const subtype = result.stopReason === 'end_turn'
|
|
158
|
-
? 'success'
|
|
159
|
-
: result.stopReason === 'cancelled'
|
|
160
|
-
? 'interrupted'
|
|
161
|
-
: 'error';
|
|
162
|
-
return {
|
|
163
|
-
type: 'result',
|
|
164
|
-
subtype,
|
|
165
|
-
session_id: sessionId,
|
|
166
|
-
...(this.hasLiveUsage ? { usage: this.liveUsageSnapshot() } : {}),
|
|
167
|
-
...(subtype === 'error'
|
|
168
|
-
? { errorMessage: `Prompt ended with stopReason: ${result.stopReason}` }
|
|
169
|
-
: {}),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Produce an error result event (for JSON-RPC errors during prompt).
|
|
174
|
-
*/
|
|
175
|
-
produceErrorResult(sessionId, errorMessage) {
|
|
176
|
-
return {
|
|
177
|
-
type: 'result',
|
|
178
|
-
subtype: 'error',
|
|
179
|
-
session_id: sessionId,
|
|
180
|
-
errorMessage,
|
|
181
|
-
...(this.hasLiveUsage ? { usage: this.liveUsageSnapshot() } : {}),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Set the operation kind (turn vs compact).
|
|
186
|
-
*/
|
|
187
|
-
setOperationKind(kind) {
|
|
188
|
-
this.operationKind = kind;
|
|
189
|
-
}
|
|
190
49
|
// =========================================================================
|
|
191
|
-
//
|
|
50
|
+
// Content channel: wire deltas → turn_diff snapshots
|
|
192
51
|
// =========================================================================
|
|
193
52
|
handleAgentMessageChunk(event, sessionId) {
|
|
194
53
|
// Use a stable key for the current text stream. Kimi ACP doesn't
|
|
@@ -214,6 +73,10 @@ export class KimiAcpTranslator {
|
|
|
214
73
|
threadId: sessionId,
|
|
215
74
|
turnId: this.currentTurnId,
|
|
216
75
|
timestamp: new Date().toISOString(),
|
|
76
|
+
// 卡片块锚点时间刷新为本次 diff 时间:'text' 是整轮累积流,工具间隙
|
|
77
|
+
// 仍会续写增量;首见时间(如 08:39)会让标题与 move-to-end 后的位置
|
|
78
|
+
// 看起来“乱序”。这里显式声明 diff 时间即块的最新写入时间。
|
|
79
|
+
refreshTimestamp: true,
|
|
217
80
|
},
|
|
218
81
|
];
|
|
219
82
|
}
|
|
@@ -233,80 +96,12 @@ export class KimiAcpTranslator {
|
|
|
233
96
|
threadId: sessionId,
|
|
234
97
|
turnId: this.currentTurnId,
|
|
235
98
|
timestamp: new Date().toISOString(),
|
|
99
|
+
refreshTimestamp: true,
|
|
236
100
|
},
|
|
237
101
|
];
|
|
238
102
|
}
|
|
239
|
-
handleToolCall(event, _sessionId) {
|
|
240
|
-
// rawInput arrives as an object on the wire; older kimi versions sent a
|
|
241
|
-
// JSON string — parse defensively in that case. The acp-server's
|
|
242
|
-
// lazy-create tool_call (first args delta, events-map.ts
|
|
243
|
-
// toolCallLazyCreateToSessionUpdate) carries NO rawInput field at all —
|
|
244
|
-
// normalize to an empty object so the card renders an empty args summary
|
|
245
|
-
// instead of crashing on undefined input (2026-08-17 live TypeError).
|
|
246
|
-
let input = event.rawInput ?? {};
|
|
247
|
-
if (typeof event.rawInput === 'string') {
|
|
248
|
-
try {
|
|
249
|
-
input = JSON.parse(event.rawInput);
|
|
250
|
-
}
|
|
251
|
-
catch {
|
|
252
|
-
// Keep raw string as-is
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
this.pendingToolCalls.set(event.toolCallId, {
|
|
256
|
-
id: event.toolCallId,
|
|
257
|
-
name: event.title,
|
|
258
|
-
input,
|
|
259
|
-
});
|
|
260
|
-
return [
|
|
261
|
-
{
|
|
262
|
-
type: 'assistant',
|
|
263
|
-
message: {
|
|
264
|
-
content: [
|
|
265
|
-
{
|
|
266
|
-
type: 'tool_use',
|
|
267
|
-
id: event.toolCallId,
|
|
268
|
-
name: event.title,
|
|
269
|
-
input,
|
|
270
|
-
},
|
|
271
|
-
],
|
|
272
|
-
},
|
|
273
|
-
timestamp: new Date().toISOString(),
|
|
274
|
-
},
|
|
275
|
-
];
|
|
276
|
-
}
|
|
277
|
-
handleToolCallUpdate(event, _sessionId) {
|
|
278
|
-
// §4.2: status:'failed' → is_error:true (fixes CLI mode's is_error always false)
|
|
279
|
-
const isError = event.status === 'failed';
|
|
280
|
-
const content = event.rawOutput ?? '';
|
|
281
|
-
return [
|
|
282
|
-
{
|
|
283
|
-
type: 'user',
|
|
284
|
-
message: {
|
|
285
|
-
content: [
|
|
286
|
-
{
|
|
287
|
-
type: 'tool_result',
|
|
288
|
-
tool_use_id: event.toolCallId,
|
|
289
|
-
content,
|
|
290
|
-
is_error: isError,
|
|
291
|
-
},
|
|
292
|
-
],
|
|
293
|
-
},
|
|
294
|
-
timestamp: new Date().toISOString(),
|
|
295
|
-
},
|
|
296
|
-
];
|
|
297
|
-
}
|
|
298
|
-
handleUsageUpdate(event) {
|
|
299
|
-
// R1: usage_update is {sessionUpdate:'usage_update', used, size}
|
|
300
|
-
// (events-map.ts:505-516) — context occupancy, not cumulative tokens.
|
|
301
|
-
this.liveUsage = {
|
|
302
|
-
total_tokens: event.used,
|
|
303
|
-
context_limit: event.size,
|
|
304
|
-
};
|
|
305
|
-
this.hasLiveUsage = true;
|
|
306
|
-
return [];
|
|
307
|
-
}
|
|
308
103
|
// =========================================================================
|
|
309
|
-
//
|
|
104
|
+
// Approval + question elicitation
|
|
310
105
|
// =========================================================================
|
|
311
106
|
handleRequestPermission(requestId, params) {
|
|
312
107
|
// §5.4 升级:question elicitation 识别(isQuestion 标记 / AskUserQuestion
|
|
@@ -349,6 +144,42 @@ export class KimiAcpTranslator {
|
|
|
349
144
|
},
|
|
350
145
|
];
|
|
351
146
|
}
|
|
147
|
+
handleOtherServerRequest(requestId, method, params) {
|
|
148
|
+
if (method !== ServerRequestMethod.ELICITATION_CREATE) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
return this.handleElicitationCreate(requestId, params);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Kimi elicitation form:requestedSchema.properties 按顺序解析为 UserQuestion[]
|
|
155
|
+
* (type:array → 多选;oneOf/anyOf const → 选项 label;title → header??question)。
|
|
156
|
+
* 题面完整文本只在 form 级 message 里合并出现(逐字段 title 可能只是短 header),
|
|
157
|
+
* 因此 message 作为卡片概要行(view.intro)透传。
|
|
158
|
+
* form 会丢弃非声明选项值 → isOther=false(卡片隐藏自定义答案输入)。
|
|
159
|
+
*/
|
|
160
|
+
handleElicitationCreate(requestId, params) {
|
|
161
|
+
const properties = params.requestedSchema?.properties ?? {};
|
|
162
|
+
const questions = [];
|
|
163
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
164
|
+
const q = elicitationPropertyToQuestion(prop);
|
|
165
|
+
if (!q) {
|
|
166
|
+
getLogger().warn(`[kimi-acp-translator] elicitation property skipped: key=${key}`);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
questions.push(q);
|
|
170
|
+
}
|
|
171
|
+
if (questions.length === 0) {
|
|
172
|
+
// 无法解析 → 交还 runner 自动响应(不悬挂服务端)。
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
const message = params.message?.trim();
|
|
176
|
+
const single = questions.length === 1;
|
|
177
|
+
const intro = message && (!single || message !== questions[0].question) ? message : undefined;
|
|
178
|
+
const event = makeQuestionApprovalEvent(requestId, questions, params.sessionId);
|
|
179
|
+
if (intro)
|
|
180
|
+
event.view.intro = intro;
|
|
181
|
+
return [event];
|
|
182
|
+
}
|
|
352
183
|
/**
|
|
353
184
|
* request_permission 兜底桥(elicitation/create 失败或旧版 kimi):
|
|
354
185
|
* 选项为 q{n}_opt_{i}(allow_once,label 直取 name)+ q{n}_skip(reject_once,
|
|
@@ -370,23 +201,6 @@ export class KimiAcpTranslator {
|
|
|
370
201
|
];
|
|
371
202
|
return [makeQuestionApprovalEvent(requestId, questions, params.sessionId)];
|
|
372
203
|
}
|
|
373
|
-
/**
|
|
374
|
-
* Derive the approval decision list from the server's offered options.
|
|
375
|
-
* accept/decline/cancel are universal; acceptForSession requires an
|
|
376
|
-
* always-class option (kimi `approve_always`, opencode `allow_always`).
|
|
377
|
-
*/
|
|
378
|
-
// =========================================================================
|
|
379
|
-
// Private helpers
|
|
380
|
-
// =========================================================================
|
|
381
|
-
/**
|
|
382
|
-
* ResultEvent.usage requires input_tokens/output_tokens: number, but the
|
|
383
|
-
* ACP wire has no such split — the live snapshot is context occupancy only.
|
|
384
|
-
* Cast is deliberate: consumers must tolerate undefined input/output (the
|
|
385
|
-
* bridge's kimi path already falls back to wire.jsonl readback).
|
|
386
|
-
*/
|
|
387
|
-
liveUsageSnapshot() {
|
|
388
|
-
return { ...this.liveUsage };
|
|
389
|
-
}
|
|
390
204
|
}
|
|
391
205
|
// =============================================================================
|
|
392
206
|
// Helpers
|
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* → synthetic init → session/prompt → consume session/update notifications
|
|
8
8
|
* until prompt settles → result event.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* stopRequested semantics, same turn idle timeout, same ConnectionLostError
|
|
13
|
-
* retry). Differences from kimi:
|
|
10
|
+
* Turn/connection/compact/approval orchestration lives in BaseAcpRunner;
|
|
11
|
+
* opencode-specific parts:
|
|
14
12
|
* - session/set_mode applies the configured mode (build/plan; default 'build'
|
|
15
13
|
* keeps manual approvals flowing); opencode sends no set_mode notification,
|
|
16
14
|
* so the client refreshes the local mode view itself
|
|
@@ -18,11 +16,15 @@
|
|
|
18
16
|
* service.ts:555-571 summarize completes before the prompt response)
|
|
19
17
|
* - text/thinking go to the assistant incremental channel (opencode wire
|
|
20
18
|
* deltas), not turn_diff snapshots
|
|
19
|
+
* - OpencodeLogErrorMonitor tails opencode's own log for LLM stream errors
|
|
20
|
+
* the ACP protocol never forwards
|
|
21
21
|
*/
|
|
22
|
-
import type { AgentKind, AgentSessionReader,
|
|
22
|
+
import type { AgentKind, AgentSessionReader, AgentStatusInfo } from '../../types.js';
|
|
23
23
|
import { JsonRpcClient } from '../../common/jsonrpc/client.js';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
24
|
+
import { OpencodeAcpTranslator } from './translator.js';
|
|
25
|
+
import { type SessionNewResult, type SessionResumeResult, type SessionPromptResult } from '../../common/acp/protocol-types.js';
|
|
26
|
+
import { type AcpPendingApproval } from '../../common/acp/protocol-helpers.js';
|
|
27
|
+
import { BaseAcpRunner } from '../../common/acp/base-acp-runner.js';
|
|
26
28
|
export interface OpencodeAcpRunnerOptions {
|
|
27
29
|
kind: AgentKind;
|
|
28
30
|
sessionReader: AgentSessionReader;
|
|
@@ -48,11 +50,7 @@ export interface OpencodeAcpRunnerOptions {
|
|
|
48
50
|
/** Tail poll interval for the opencode log error monitor. Defaults to 2000ms. */
|
|
49
51
|
errorMonitorPollIntervalMs?: number;
|
|
50
52
|
}
|
|
51
|
-
export declare class OpencodeAcpRunner extends
|
|
52
|
-
private connectionManager;
|
|
53
|
-
private currentTranslator;
|
|
54
|
-
private activeSessionId;
|
|
55
|
-
private model?;
|
|
53
|
+
export declare class OpencodeAcpRunner extends BaseAcpRunner<OpencodeAcpTranslator> {
|
|
56
54
|
/** Configured session mode applied via session/set_mode (§P5). */
|
|
57
55
|
private configuredMode;
|
|
58
56
|
/** Current session mode id, parsed from session/new|resume configOptions. */
|
|
@@ -62,22 +60,12 @@ export declare class OpencodeAcpRunner extends ConnectionBasedRunner<JsonRpcClie
|
|
|
62
60
|
/** Tails opencode's own log to surface LLM stream errors the ACP protocol
|
|
63
61
|
* never forwards (quota/limit errors back off for ~1h → turn would hang). */
|
|
64
62
|
private readonly errorMonitor;
|
|
65
|
-
/** Pending approval requests: requestId → kind + view + options. */
|
|
66
|
-
private pendingApprovals;
|
|
67
|
-
/** Tracker for the in-flight prompt request — needed for cancellation. */
|
|
68
|
-
private promptSettled;
|
|
69
|
-
/** Whether the prompt request has been fired (distinguishes "stop before
|
|
70
|
-
* prompt sent" from "stop while prompt in-flight" — the latter sends
|
|
71
|
-
* session/cancel immediately). */
|
|
72
|
-
private promptSent;
|
|
73
63
|
constructor(opts: OpencodeAcpRunnerOptions);
|
|
74
64
|
protected get logTag(): string;
|
|
75
65
|
protected get turnTimeoutErrorMessage(): string;
|
|
76
66
|
protected get turnInterruptedErrorMessage(): string;
|
|
77
|
-
protected
|
|
67
|
+
protected get connectionClosedMessage(): string;
|
|
78
68
|
protected shouldDeferStop(): boolean;
|
|
79
|
-
protected cancelCurrentTurn(): Promise<void>;
|
|
80
|
-
protected clearTurnState(): void;
|
|
81
69
|
/**
|
|
82
70
|
* Start watching opencode's own log for LLM stream errors of the active
|
|
83
71
|
* session. opencode ACP does not surface retry/error state to the client
|
|
@@ -88,23 +76,14 @@ export declare class OpencodeAcpRunner extends ConnectionBasedRunner<JsonRpcClie
|
|
|
88
76
|
* until the idle watchdog.
|
|
89
77
|
*/
|
|
90
78
|
private startErrorMonitor;
|
|
91
|
-
protected
|
|
92
|
-
protected
|
|
93
|
-
protected disposeConnections(): Promise<void>;
|
|
79
|
+
protected createTranslator(): OpencodeAcpTranslator;
|
|
80
|
+
protected applyTurnSettings(client: JsonRpcClient, sessionId: string): Promise<void>;
|
|
94
81
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* opencode special-cases the `/compact` text prompt server-side
|
|
98
|
-
* (service.ts:555-571): it runs session.summarize synchronously and the
|
|
99
|
-
* prompt response settles only after compaction completes — no background
|
|
100
|
-
* record polling needed (unlike kimi).
|
|
101
|
-
*/
|
|
102
|
-
runCompact(_message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
|
|
103
|
-
/**
|
|
104
|
-
* Respond to an approval server request. `response` is the bridge
|
|
105
|
-
* ApprovalAction (`{ action: 'accept' | 'decline' | 'cancel' }`).
|
|
82
|
+
* opencode /compact is synchronous server-side — standard prompt-settle
|
|
83
|
+
* translation (no wire.jsonl polling, unlike kimi).
|
|
106
84
|
*/
|
|
107
|
-
|
|
85
|
+
protected onCompactPromptFired(promptPromise: Promise<SessionPromptResult>, translator: OpencodeAcpTranslator, sessionId: string, _opts: unknown, _baselineTotalCount: number): void;
|
|
86
|
+
protected buildApprovalOutcome(action: string, pending: AcpPendingApproval, _response: unknown): unknown;
|
|
108
87
|
getStatusInfo(): AgentStatusInfo;
|
|
109
88
|
/**
|
|
110
89
|
* Hot-apply a mode change to the live ACP session (§P5).
|
|
@@ -121,18 +100,15 @@ export declare class OpencodeAcpRunner extends ConnectionBasedRunner<JsonRpcClie
|
|
|
121
100
|
updateApprovalMode(settings: {
|
|
122
101
|
mode?: 'build' | 'plan';
|
|
123
102
|
}): Promise<void>;
|
|
124
|
-
/**
|
|
125
|
-
* Acquire the connection and set up the turn:
|
|
126
|
-
* session/new (new session) or session/resume (existing sessionId)
|
|
127
|
-
* → session/prompt.
|
|
128
|
-
*/
|
|
129
|
-
protected setupTurn(message: string, opts: SpawnOptions): Promise<void>;
|
|
130
103
|
/**
|
|
131
104
|
* Track the current session mode and model from a session/new|resume
|
|
132
105
|
* result's configOptions (opencode buildModeSelectOption/buildModelSelectOption:
|
|
133
106
|
* {id, category, currentValue}).
|
|
134
107
|
*/
|
|
135
108
|
private trackConfigOptions;
|
|
136
|
-
|
|
137
|
-
|
|
109
|
+
protected onTurnSessionReady(sessionId: string): void;
|
|
110
|
+
protected onSessionEstablished(result: SessionResumeResult | SessionNewResult): void;
|
|
111
|
+
protected onClearTurnState(): void;
|
|
112
|
+
protected handleNotification(method: string, params: unknown): void;
|
|
113
|
+
protected handleServerRequest(id: number | string, method: string, params: unknown): void;
|
|
138
114
|
}
|