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.
Files changed (136) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/bridge/approval-actions.d.ts +1 -1
  3. package/dist/bridge/approval-actions.js +1 -1
  4. package/dist/bridge/bash-run.d.ts +71 -0
  5. package/dist/bridge/bash-run.js +217 -0
  6. package/dist/bridge/compact.d.ts +155 -0
  7. package/dist/bridge/compact.js +450 -0
  8. package/dist/bridge/final-usage.js +4 -4
  9. package/dist/bridge/inbound-media.js +72 -46
  10. package/dist/bridge/index.d.ts +106 -153
  11. package/dist/bridge/index.js +400 -1000
  12. package/dist/bridge/job-watch.d.ts +68 -0
  13. package/dist/bridge/job-watch.js +146 -0
  14. package/dist/bridge/queue-manager.d.ts +3 -3
  15. package/dist/bridge/queue-manager.js +10 -29
  16. package/dist/card/approval-render.d.ts +2 -2
  17. package/dist/card/approval-render.js +141 -130
  18. package/dist/card/card-budget.js +123 -98
  19. package/dist/card/card-helpers.d.ts +3 -3
  20. package/dist/card/card-helpers.js +6 -6
  21. package/dist/card/final-answer-file.js +1 -1
  22. package/dist/card/run-renderer.d.ts +3 -3
  23. package/dist/card/run-renderer.js +114 -159
  24. package/dist/card/run-state.d.ts +1 -15
  25. package/dist/card/run-state.js +358 -292
  26. package/dist/card/tool-render.d.ts +1 -1
  27. package/dist/card/tool-render.js +21 -17
  28. package/dist/card/usage-format.d.ts +22 -7
  29. package/dist/card/usage-format.js +119 -100
  30. package/dist/cli.js +15 -3
  31. package/dist/clone.js +2 -2
  32. package/dist/common/text-width.js +36 -24
  33. package/dist/common/tool-summary.js +132 -90
  34. package/dist/config/codex-config.js +130 -106
  35. package/dist/config/index.d.ts +16 -2
  36. package/dist/config/index.js +23 -8
  37. package/dist/connector/index.js +6 -6
  38. package/dist/error-classification.js +3 -2
  39. package/dist/inbound/placeholder.d.ts +1 -1
  40. package/dist/inbound/placeholder.js +1 -1
  41. package/dist/index.js +387 -281
  42. package/dist/logger/index.js +6 -6
  43. package/dist/message-handlers.d.ts +0 -5
  44. package/dist/message-handlers.js +254 -237
  45. package/dist/model-catalog.d.ts +2 -2
  46. package/dist/model-catalog.js +3 -8
  47. package/dist/platform/identity.js +1 -1
  48. package/dist/platform/path.d.ts +1 -1
  49. package/dist/platform/path.js +1 -1
  50. package/dist/platform/sleep-blocker.js +1 -1
  51. package/dist/platform/terminator-win32.js +2 -1
  52. package/dist/router/commands/ls.d.ts +3 -0
  53. package/dist/router/commands/ls.js +256 -175
  54. package/dist/router/commands/order.js +184 -159
  55. package/dist/router/commands/resume.js +190 -152
  56. package/dist/router/commands/ws.js +219 -174
  57. package/dist/router/config/dsh.d.ts +4 -8
  58. package/dist/router/config/dsh.js +3 -10
  59. package/dist/router/config/kimi.js +1 -1
  60. package/dist/router/config/types.d.ts +1 -1
  61. package/dist/router/config-diff.js +1 -1
  62. package/dist/router/index.d.ts +56 -11
  63. package/dist/router/index.js +401 -391
  64. package/dist/runner/bash/runner.js +2 -4
  65. package/dist/runner/claude/session.d.ts +1 -1
  66. package/dist/runner/claude/session.js +6 -4
  67. package/dist/runner/codex/app-server/runner.d.ts +9 -2
  68. package/dist/runner/codex/app-server/runner.js +30 -25
  69. package/dist/runner/codex/app-server/translator.d.ts +2 -5
  70. package/dist/runner/codex/app-server/translator.js +291 -238
  71. package/dist/runner/common/acp/acp-approval.d.ts +1 -1
  72. package/dist/runner/common/acp/acp-approval.js +1 -1
  73. package/dist/runner/common/acp/base-acp-runner.d.ts +5 -2
  74. package/dist/runner/common/acp/base-acp-runner.js +26 -65
  75. package/dist/runner/common/acp/base-translator.d.ts +12 -2
  76. package/dist/runner/common/acp/base-translator.js +50 -26
  77. package/dist/runner/common/acp/protocol-types.d.ts +1 -1
  78. package/dist/runner/common/acp/protocol-types.js +2 -2
  79. package/dist/runner/common/connection-based-runner.d.ts +32 -1
  80. package/dist/runner/common/connection-based-runner.js +32 -2
  81. package/dist/runner/common/jsonl-stream.js +154 -187
  82. package/dist/runner/common/jsonrpc/client.d.ts +29 -0
  83. package/dist/runner/common/jsonrpc/client.js +43 -12
  84. package/dist/runner/common/jsonrpc/connection-manager.d.ts +2 -2
  85. package/dist/runner/common/jsonrpc/connection-manager.js +4 -4
  86. package/dist/runner/common/spawning-runner.d.ts +6 -1
  87. package/dist/runner/common/spawning-runner.js +9 -4
  88. package/dist/runner/common/turn-started.d.ts +8 -0
  89. package/dist/runner/common/turn-started.js +11 -0
  90. package/dist/runner/dsh/approval.js +84 -51
  91. package/dist/runner/dsh/catalog-probe.d.ts +17 -0
  92. package/dist/runner/dsh/catalog-probe.js +10 -0
  93. package/dist/runner/dsh/client.d.ts +7 -11
  94. package/dist/runner/dsh/client.js +356 -289
  95. package/dist/runner/dsh/exit-cancel.d.ts +2 -2
  96. package/dist/runner/dsh/exit-cancel.js +2 -2
  97. package/dist/runner/dsh/inflight.d.ts +1 -1
  98. package/dist/runner/dsh/inflight.js +1 -1
  99. package/dist/runner/dsh/mux-stream.js +28 -8
  100. package/dist/runner/dsh/runner.d.ts +44 -4
  101. package/dist/runner/dsh/runner.js +296 -199
  102. package/dist/runner/dsh/translator.d.ts +1 -31
  103. package/dist/runner/dsh/translator.js +180 -178
  104. package/dist/runner/index.d.ts +1 -1
  105. package/dist/runner/kimi/acp/runner.d.ts +1 -2
  106. package/dist/runner/kimi/acp/runner.js +82 -46
  107. package/dist/runner/kimi/acp/translator.d.ts +1 -1
  108. package/dist/runner/kimi/acp/translator.js +3 -4
  109. package/dist/runner/opencode/acp/error-monitor.js +5 -5
  110. package/dist/runner/opencode/acp/runner.d.ts +2 -2
  111. package/dist/runner/opencode/acp/runner.js +4 -4
  112. package/dist/runner/pi/rpc/client.js +4 -13
  113. package/dist/runner/pi/rpc/protocol-types.d.ts +1 -1
  114. package/dist/runner/pi/rpc/protocol-types.js +1 -1
  115. package/dist/runner/pi/rpc/runner.d.ts +3 -0
  116. package/dist/runner/pi/rpc/runner.js +27 -42
  117. package/dist/runner/pi/rpc/translator.js +61 -60
  118. package/dist/runner/types.d.ts +2 -2
  119. package/dist/runner/types.js +1 -1
  120. package/dist/session/claude/sessions.d.ts +45 -0
  121. package/dist/session/claude/sessions.js +160 -122
  122. package/dist/session/codex/rollout-reader.js +181 -182
  123. package/dist/session/common/throw-session-list-error.d.ts +10 -0
  124. package/dist/session/common/throw-session-list-error.js +16 -0
  125. package/dist/session/dsh/sessions.js +291 -235
  126. package/dist/session/kimi/sessions.d.ts +40 -8
  127. package/dist/session/kimi/sessions.js +373 -301
  128. package/dist/session/opencode/sessions.d.ts +142 -1
  129. package/dist/session/opencode/sessions.js +378 -321
  130. package/dist/session/pi/sessions.js +4 -5
  131. package/dist/session/session-store.d.ts +9 -1
  132. package/dist/session/session-store.js +99 -99
  133. package/dist/update/version-check.js +2 -1
  134. package/dist/workspace/index.d.ts +1 -1
  135. package/dist/workspace/index.js +1 -1
  136. 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
- export class DshTranslator {
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
- /**
38
- * 本 turn 末条 durable assistant 消息的正文(**完整**、未截断)。
39
- *
40
- * live chunk 是逐 delta 拼接、会被状态层的 MAX_TEXT_CHARS 窗口截断;
41
- * durable `assistant/message` 一次就到齐全文。收 turn 时把它挂在 result 上,
42
- * 卡片才能渲染完整回答(2026-11-01 输出块被截断缺陷)。
43
- */
44
- lastMessageText;
45
- /** Translate one live assistant-stream chunk into AgentEvents. */
46
- streamFrameToAgentEvents(frame, _sessionId) {
47
- if (frame.type === 'start') {
48
- if (frame.attemptId)
49
- this.attemptId = frame.attemptId;
50
- return [];
51
- }
52
- if (frame.type !== 'chunk' || !frame.chunk)
53
- return [];
54
- return this.chunkToAgentEvents(frame.chunk, frame.time ?? Date.now(), frame.attemptId ?? this.attemptId);
55
- }
56
- /**
57
- * Record durable per-step usage — the accumulation source. assistant/message
58
- * carries exactly one usage per model call and is seq-deduped by the client,
59
- * so summing it gives the turn total without double counting.
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
- * Record a live usage chunk. Never summed: the same step also arrives as a
71
- * durable assistant/message.usage, so summing both would double count. It only
72
- * refreshes the watermark and covers a turn whose messages never persisted.
73
- */
74
- recordStreamUsage(usage) {
75
- this.lastUsage = usage;
76
- this.hasStreamUsage = true;
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
- chunkToAgentEvents(chunk, time, attemptId = '0') {
79
- if (chunk.type === 'usage' && chunk.usage && typeof chunk.usage === 'object') {
80
- this.recordStreamUsage(chunk.usage);
81
- return [];
82
- }
83
- const ts = new Date(time).toISOString();
84
- if (chunk.type === 'text-delta' && typeof chunk.text === 'string' && chunk.text) {
85
- return [
86
- {
87
- type: 'assistant',
88
- message: { content: [{ type: 'text', text: chunk.text }] },
89
- timestamp: ts,
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
- /** Translate one session event into AgentEvents (may be empty). */
114
- eventToAgentEvents(ev, sessionId) {
115
- switch (ev.type) {
116
- case 'assistant/chunk': {
117
- const chunk = ev.data.chunk;
118
- if (!chunk)
119
- return [];
120
- return this.chunkToAgentEvents(chunk, ev.time);
121
- }
122
- case 'assistant/message': {
123
- const raw = ev.data.usage;
124
- if (raw && typeof raw === 'object') {
125
- this.recordMessageUsage(raw);
126
- }
127
- // 每条 durable 消息都带完整正文;记最后一条非空的就是本轮回答。
128
- const text = messageText(ev.data.message);
129
- if (text.trim())
130
- this.lastMessageText = text;
131
- return [];
132
- }
133
- case 'tool/call': {
134
- // DSH 把「模型发起调用」落成独立事件(name/arguments 是模型原始输出)。
135
- // 这是卡片工具行的唯一来源:assistant/message 里只有完成后的正文块,
136
- // 不含调用轨迹。
137
- const call = ev.data;
138
- const id = typeof call.callId === 'string' ? call.callId : '';
139
- const name = typeof call.name === 'string' ? call.name : '';
140
- if (!id || !name)
141
- return [];
142
- return [
143
- {
144
- type: 'assistant',
145
- message: {
146
- content: [{ type: 'tool_use', id, name, input: parseToolArguments(call.arguments) }],
147
- },
148
- timestamp: new Date(ev.time).toISOString(),
149
- },
150
- ];
151
- }
152
- case 'tool/result': {
153
- const data = ev.data;
154
- const msg = data.message;
155
- const toolCallId = typeof msg?.toolCallId === 'string' ? msg.toolCallId : '';
156
- if (!toolCallId)
157
- return [];
158
- return [
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: 'user',
161
- message: {
162
- content: [
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
- case 'turn/end': {
176
- const turnReason = ev.data.reason;
177
- const reason = turnReason?.kind;
178
- const subtype = reason === 'completed'
179
- ? 'success'
180
- : reason === 'aborted' || reason === 'interrupted' || reason === 'forked'
181
- ? 'interrupted'
182
- : 'error';
183
- const result = {
184
- type: 'result',
185
- subtype,
186
- session_id: sessionId,
187
- timestamp: new Date(ev.time).toISOString(),
188
- };
189
- if (this.hasMessageUsage) {
190
- // 本 turn 各采样之和(多步工具往返 = 多条 assistant/message)。
191
- result.usage = mapAggregatedUsage(this.turnUsage);
192
- }
193
- else if (this.hasStreamUsage && this.lastUsage) {
194
- // 只有 live usage chunk(消息未落盘,如启动期中断):退回末次采样。
195
- result.usage = mapUsage(this.lastUsage);
196
- }
197
- if (subtype === 'error') {
198
- const detail = turnReason?.error?.message;
199
- result.errorMessage = detail
200
- ? `turn ended with reason: ${reason}: ${detail}`
201
- : `turn ended with reason: ${reason}`;
202
- }
203
- if (this.lastMessageText) {
204
- result.finalAnswerText = this.lastMessageText;
205
- }
206
- return [result];
207
- }
208
- default:
209
- return [];
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
  /**
@@ -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
- if (action === 'answer') {
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
- if (action === 'decline') {
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 live 2026-08-15/16 against kimi 0.36.0):
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 live 2026-08-15/16 against kimi 0.36.0):
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
- // 命令恢复(2026-09-13 对 kimi 0.42.0 实测):审批门在 tool.call.started
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 { closeSync, openSync, readSync, statSync } from 'node:fs';
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
- * 每次的等待。2026-10-30 现场:daemon 刚重启,`/api/model` 因模型目录拉取
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
- * 同一个窗口(2026-10-30 现场:照提示重启两次仍然失败,等几分钟自己好了)。
108
+ * 同一个窗口(照提示重启两次仍然失败,等几分钟自己好了)。
109
109
  */
110
110
  protected enrichSessionSetupError(err: unknown): Error;
111
111
  getStatusInfo(): AgentStatusInfo;