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
package/dist/card/run-state.js
CHANGED
|
@@ -33,6 +33,10 @@ export function reduceRunState(state, event) {
|
|
|
33
33
|
return reducePlanEvent(state, event);
|
|
34
34
|
if (event.type === 'file_change')
|
|
35
35
|
return reduceFileChangeEvent(state, event);
|
|
36
|
+
if (event.type === 'notice')
|
|
37
|
+
return reduceNoticeEvent(state, event);
|
|
38
|
+
if (event.type === 'session_info')
|
|
39
|
+
return reduceSessionInfoEvent(state, event);
|
|
36
40
|
if (event.type === 'turn_started')
|
|
37
41
|
return reduceTurnStartedEvent(state, event);
|
|
38
42
|
if (event.type === 'turn_diff')
|
|
@@ -97,96 +101,116 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
97
101
|
const complete = event.complete === true;
|
|
98
102
|
if (event.text !== undefined) {
|
|
99
103
|
const blocks = markThinkingInactive(next.blocks);
|
|
104
|
+
const capped = upsertSnapshotBlock(blocks, (b) => b.kind === 'text' && b.itemId === event.itemId, () => ({
|
|
105
|
+
kind: 'text',
|
|
106
|
+
itemId: event.itemId,
|
|
107
|
+
content: keepTailMarked(event.text, MAX_TEXT_CHARS),
|
|
108
|
+
timestamp: ts,
|
|
109
|
+
...(complete ? { completedAt: ts } : {}),
|
|
110
|
+
}), (existing) => {
|
|
111
|
+
const textBlock = existing;
|
|
112
|
+
return {
|
|
113
|
+
...textBlock,
|
|
114
|
+
content: keepTailMarked(event.text, MAX_TEXT_CHARS),
|
|
115
|
+
// kimi:块锚点时间改为本次 diff 时间(整轮累积流不断续写,
|
|
116
|
+
// 标题应反映“最后写入时刻”而非首见时刻;move-to-end 后位置
|
|
117
|
+
// 与标题才一致)。codex app-server 不置 refreshTimestamp,
|
|
118
|
+
// 保持“首见时间戳不刷新”的既有契约。
|
|
119
|
+
...(event.refreshTimestamp ? { timestamp: ts } : {}),
|
|
120
|
+
...(complete ? { completedAt: ts } : {}),
|
|
121
|
+
};
|
|
122
|
+
}, !complete);
|
|
100
123
|
next = {
|
|
101
124
|
...next,
|
|
102
|
-
blocks: keepLatestBlocks(
|
|
103
|
-
|
|
104
|
-
itemId: event.itemId,
|
|
105
|
-
content: keepTail(event.text, MAX_TEXT_CHARS),
|
|
106
|
-
timestamp: ts,
|
|
107
|
-
...(complete ? { completedAt: ts } : {}),
|
|
108
|
-
}), (existing) => {
|
|
109
|
-
const textBlock = existing;
|
|
110
|
-
return {
|
|
111
|
-
...textBlock,
|
|
112
|
-
content: keepTail(event.text, MAX_TEXT_CHARS),
|
|
113
|
-
...(complete ? { completedAt: ts } : {}),
|
|
114
|
-
};
|
|
115
|
-
}, !complete)),
|
|
125
|
+
blocks: keepLatestBlocks(capped),
|
|
126
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
116
127
|
footer: 'streaming',
|
|
117
128
|
};
|
|
118
129
|
}
|
|
119
130
|
if (event.reasoning !== undefined) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'thinking' && b.itemId === event.itemId, () => ({
|
|
132
|
+
kind: 'thinking',
|
|
133
|
+
itemId: event.itemId,
|
|
134
|
+
content: keepTailMarked(event.reasoning, MAX_REASONING_CHARS),
|
|
135
|
+
active: !complete,
|
|
136
|
+
timestamp: ts,
|
|
137
|
+
...(complete ? { completedAt: ts } : {}),
|
|
138
|
+
}), (existing) => {
|
|
139
|
+
const thinkingBlock = existing;
|
|
140
|
+
return {
|
|
141
|
+
...thinkingBlock,
|
|
142
|
+
content: keepTailMarked(event.reasoning, MAX_REASONING_CHARS),
|
|
126
143
|
active: !complete,
|
|
127
|
-
timestamp: ts,
|
|
144
|
+
...(event.refreshTimestamp ? { timestamp: ts } : {}),
|
|
128
145
|
...(complete ? { completedAt: ts } : {}),
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
...(complete ? { completedAt: ts } : {}),
|
|
136
|
-
};
|
|
137
|
-
}, !complete)),
|
|
146
|
+
};
|
|
147
|
+
}, !complete);
|
|
148
|
+
next = {
|
|
149
|
+
...next,
|
|
150
|
+
blocks: keepLatestBlocks(capped),
|
|
151
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
138
152
|
footer: 'thinking',
|
|
139
153
|
};
|
|
140
154
|
}
|
|
141
155
|
if (event.toolOutput !== undefined) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
156
|
+
const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'tool' && b.tool.id === event.itemId, () => {
|
|
157
|
+
// 身份只在首见时写入(existing 分支不动):toolName/toolInput/toolHint。
|
|
158
|
+
const inputStr = truncateDetail(stringifyUnknown(event.toolInput ?? {}));
|
|
159
|
+
return {
|
|
145
160
|
kind: 'tool',
|
|
146
161
|
tool: {
|
|
147
162
|
id: event.itemId,
|
|
148
|
-
name: 'command',
|
|
149
|
-
input:
|
|
163
|
+
name: event.toolName ?? 'command',
|
|
164
|
+
input: inputStr,
|
|
165
|
+
parsedInput: tryParseRecord(inputStr),
|
|
166
|
+
...(event.toolHint !== undefined ? { toolHint: event.toolHint } : {}),
|
|
150
167
|
output: keepTail(event.toolOutput, MAX_TOOL_DETAIL_CHARS),
|
|
151
168
|
status: complete ? (event.toolStatus ?? 'ok') : 'running',
|
|
152
169
|
startedAt: ts,
|
|
153
170
|
...(complete ? { completedAt: ts } : {}),
|
|
154
171
|
},
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
},
|
|
165
|
-
}
|
|
172
|
+
};
|
|
173
|
+
}, (existing) => ({
|
|
174
|
+
kind: 'tool',
|
|
175
|
+
tool: {
|
|
176
|
+
...existing.tool,
|
|
177
|
+
output: keepTail(event.toolOutput, MAX_TOOL_DETAIL_CHARS),
|
|
178
|
+
status: complete
|
|
179
|
+
? (event.toolStatus ?? 'ok')
|
|
180
|
+
: existing.tool.status,
|
|
181
|
+
...(complete ? { completedAt: ts } : {}),
|
|
182
|
+
},
|
|
183
|
+
}), !complete);
|
|
184
|
+
next = {
|
|
185
|
+
...next,
|
|
186
|
+
blocks: keepLatestBlocks(capped),
|
|
187
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
166
188
|
footer: 'tool_running',
|
|
167
189
|
};
|
|
168
190
|
}
|
|
169
191
|
if (event.plan !== undefined) {
|
|
170
|
-
const newPlan =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
192
|
+
const newPlan = keepTailMarked(event.plan, MAX_REASONING_CHARS * 2);
|
|
193
|
+
const capped = upsertSnapshotBlock(next.blocks, (b) => b.kind === 'plan' && b.itemId === event.itemId, () => ({
|
|
194
|
+
kind: 'plan',
|
|
195
|
+
itemId: event.itemId,
|
|
196
|
+
content: newPlan,
|
|
197
|
+
active: !complete,
|
|
198
|
+
timestamp: ts,
|
|
199
|
+
...(complete ? { completedAt: ts } : {}),
|
|
200
|
+
}), (existing) => {
|
|
201
|
+
const planBlock = existing;
|
|
202
|
+
return {
|
|
203
|
+
...planBlock,
|
|
177
204
|
content: newPlan,
|
|
178
205
|
active: !complete,
|
|
179
|
-
timestamp: ts,
|
|
180
206
|
...(complete ? { completedAt: ts } : {}),
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
};
|
|
189
|
-
}, !complete)),
|
|
207
|
+
};
|
|
208
|
+
}, !complete);
|
|
209
|
+
next = {
|
|
210
|
+
...next,
|
|
211
|
+
plan: newPlan,
|
|
212
|
+
blocks: keepLatestBlocks(capped),
|
|
213
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
190
214
|
};
|
|
191
215
|
}
|
|
192
216
|
if (event.fileChanges !== undefined) {
|
|
@@ -204,7 +228,12 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
204
228
|
// 同一 item 的同一路径原地替换(权威 diff 校正),新路径/新 item 追加。
|
|
205
229
|
blocks = upsertSnapshotBlock(blocks, (b) => b.kind === 'file_change' && b.itemId === event.itemId && b.path === fc.path, () => fileBlock, () => fileBlock, !complete);
|
|
206
230
|
}
|
|
207
|
-
|
|
231
|
+
const capped = blocks;
|
|
232
|
+
next = {
|
|
233
|
+
...next,
|
|
234
|
+
blocks: keepLatestBlocks(capped),
|
|
235
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
236
|
+
};
|
|
208
237
|
}
|
|
209
238
|
return next;
|
|
210
239
|
}
|
|
@@ -237,7 +266,7 @@ function reduceSystemEvent(state, event) {
|
|
|
237
266
|
if (event.type !== 'system')
|
|
238
267
|
return state;
|
|
239
268
|
if (event.subtype === 'init') {
|
|
240
|
-
return { ...state, sessionId: event.session_id, footer: 'thinking' };
|
|
269
|
+
return { ...state, sessionId: event.session_id, model: event.model, footer: 'thinking' };
|
|
241
270
|
}
|
|
242
271
|
if (event.subtype === 'compact_boundary' && event.compactMetadata) {
|
|
243
272
|
return {
|
|
@@ -285,6 +314,10 @@ function reduceResultEvent(state, event) {
|
|
|
285
314
|
blocks: markThinkingInactive(state.blocks),
|
|
286
315
|
resultSubtype: mergedSubtype,
|
|
287
316
|
errorMsg: mergedErrorMsg,
|
|
317
|
+
// costUsd:首个非 undefined 胜出(与 errorMsg 合并语义一致)
|
|
318
|
+
...(event.total_cost_usd !== undefined && state.costUsd === undefined
|
|
319
|
+
? { costUsd: event.total_cost_usd }
|
|
320
|
+
: {}),
|
|
288
321
|
};
|
|
289
322
|
}
|
|
290
323
|
/** Handle assistant thinking/text/tool_use content blocks. */
|
|
@@ -300,7 +333,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
300
333
|
...next.blocks.slice(0, -1),
|
|
301
334
|
{
|
|
302
335
|
kind: 'thinking',
|
|
303
|
-
content:
|
|
336
|
+
content: keepTailMarked(last.content + '\n' + content.thinking, MAX_REASONING_CHARS),
|
|
304
337
|
active: true,
|
|
305
338
|
timestamp: last.timestamp ?? event.timestamp,
|
|
306
339
|
},
|
|
@@ -309,7 +342,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
309
342
|
...next.blocks,
|
|
310
343
|
{
|
|
311
344
|
kind: 'thinking',
|
|
312
|
-
content:
|
|
345
|
+
content: keepTailMarked(content.thinking, MAX_REASONING_CHARS),
|
|
313
346
|
active: true,
|
|
314
347
|
timestamp: event.timestamp,
|
|
315
348
|
},
|
|
@@ -317,6 +350,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
317
350
|
next = {
|
|
318
351
|
...next,
|
|
319
352
|
blocks: keepLatestBlocks(newBlocks),
|
|
353
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
320
354
|
footer: 'thinking',
|
|
321
355
|
};
|
|
322
356
|
}
|
|
@@ -328,7 +362,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
328
362
|
...blocks.slice(0, -1),
|
|
329
363
|
{
|
|
330
364
|
kind: 'text',
|
|
331
|
-
content:
|
|
365
|
+
content: keepTailMarked(last.content + content.text, MAX_TEXT_CHARS),
|
|
332
366
|
timestamp: last.timestamp ?? event.timestamp,
|
|
333
367
|
},
|
|
334
368
|
]
|
|
@@ -336,13 +370,14 @@ function reduceAssistantEvent(state, event) {
|
|
|
336
370
|
...blocks,
|
|
337
371
|
{
|
|
338
372
|
kind: 'text',
|
|
339
|
-
content:
|
|
373
|
+
content: keepTailMarked(content.text, MAX_TEXT_CHARS),
|
|
340
374
|
timestamp: event.timestamp,
|
|
341
375
|
},
|
|
342
376
|
];
|
|
343
377
|
next = {
|
|
344
378
|
...next,
|
|
345
379
|
blocks: keepLatestBlocks(newBlocks),
|
|
380
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
346
381
|
footer: 'streaming',
|
|
347
382
|
};
|
|
348
383
|
}
|
|
@@ -353,22 +388,25 @@ function reduceAssistantEvent(state, event) {
|
|
|
353
388
|
// string as the old per-render parse, so truncation-broken JSON still
|
|
354
389
|
// yields null (over-cap input renders no summary — behavior preserved).
|
|
355
390
|
const inputStr = truncateDetail(stringifyUnknown(content.input));
|
|
391
|
+
const capped = [
|
|
392
|
+
...markThinkingInactive(next.blocks),
|
|
393
|
+
{
|
|
394
|
+
kind: 'tool',
|
|
395
|
+
tool: {
|
|
396
|
+
id: content.id,
|
|
397
|
+
name: content.name,
|
|
398
|
+
input: inputStr,
|
|
399
|
+
parsedInput: tryParseRecord(inputStr),
|
|
400
|
+
...(content.summary !== undefined ? { summary: content.summary } : {}),
|
|
401
|
+
status: 'running',
|
|
402
|
+
startedAt: event.timestamp,
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
];
|
|
356
406
|
next = {
|
|
357
407
|
...next,
|
|
358
|
-
blocks: keepLatestBlocks(
|
|
359
|
-
|
|
360
|
-
{
|
|
361
|
-
kind: 'tool',
|
|
362
|
-
tool: {
|
|
363
|
-
id: content.id,
|
|
364
|
-
name: content.name,
|
|
365
|
-
input: inputStr,
|
|
366
|
-
parsedInput: tryParseRecord(inputStr),
|
|
367
|
-
status: 'running',
|
|
368
|
-
startedAt: event.timestamp,
|
|
369
|
-
},
|
|
370
|
-
},
|
|
371
|
-
]),
|
|
408
|
+
blocks: keepLatestBlocks(capped),
|
|
409
|
+
omittedBlocks: (next.omittedBlocks ?? 0) + Math.max(0, capped.length - MAX_BLOCKS),
|
|
372
410
|
footer: 'tool_running',
|
|
373
411
|
};
|
|
374
412
|
}
|
|
@@ -409,12 +447,39 @@ function reduceToolResultEvent(state, event) {
|
|
|
409
447
|
}
|
|
410
448
|
return next;
|
|
411
449
|
}
|
|
450
|
+
/** Handle notice event — append a runtime notice (warning / reroute / compaction). */
|
|
451
|
+
function reduceNoticeEvent(state, event) {
|
|
452
|
+
if (event.type !== 'notice')
|
|
453
|
+
return state;
|
|
454
|
+
return {
|
|
455
|
+
...state,
|
|
456
|
+
notices: [
|
|
457
|
+
...(state.notices ?? []),
|
|
458
|
+
{
|
|
459
|
+
level: event.level,
|
|
460
|
+
...(event.code !== undefined ? { code: event.code } : {}),
|
|
461
|
+
text: event.text,
|
|
462
|
+
...(event.timestamp !== undefined ? { timestamp: event.timestamp } : {}),
|
|
463
|
+
},
|
|
464
|
+
],
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
/** Handle session_info event — update sessionTitle / mode only when present. */
|
|
468
|
+
function reduceSessionInfoEvent(state, event) {
|
|
469
|
+
if (event.type !== 'session_info')
|
|
470
|
+
return state;
|
|
471
|
+
return {
|
|
472
|
+
...state,
|
|
473
|
+
...(event.title !== undefined ? { sessionTitle: event.title } : {}),
|
|
474
|
+
...(event.mode !== undefined ? { mode: event.mode } : {}),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
412
477
|
/** Handle plan event — accumulate plan text for Codex-style agents. */
|
|
413
478
|
function reducePlanEvent(state, event) {
|
|
414
479
|
if (event.type !== 'plan')
|
|
415
480
|
return state;
|
|
416
481
|
const currentPlan = state.plan ?? '';
|
|
417
|
-
const newPlan =
|
|
482
|
+
const newPlan = keepTailMarked(currentPlan + '\n' + event.plan, MAX_REASONING_CHARS * 2);
|
|
418
483
|
// Plan is a single evolving document — replace the existing plan block
|
|
419
484
|
// instead of appending a new one (avoids N overlapping blocks after N events).
|
|
420
485
|
const withoutOldPlan = state.blocks.filter((b) => b.kind !== 'plan');
|
|
@@ -426,6 +491,7 @@ function reducePlanEvent(state, event) {
|
|
|
426
491
|
...state,
|
|
427
492
|
plan: newPlan,
|
|
428
493
|
blocks: keepLatestBlocks(newBlocks),
|
|
494
|
+
omittedBlocks: (state.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
429
495
|
};
|
|
430
496
|
}
|
|
431
497
|
/** Handle file_change event — emit a file_change block for rendering (Codex-style agents). */
|
|
@@ -446,6 +512,7 @@ function reduceFileChangeEvent(state, event) {
|
|
|
446
512
|
return {
|
|
447
513
|
...state,
|
|
448
514
|
blocks: keepLatestBlocks(newBlocks),
|
|
515
|
+
omittedBlocks: (state.omittedBlocks ?? 0) + Math.max(0, newBlocks.length - MAX_BLOCKS),
|
|
449
516
|
};
|
|
450
517
|
}
|
|
451
518
|
export function finishRun(state, terminal, meta = {}) {
|
|
@@ -522,6 +589,27 @@ function tryParseRecord(input) {
|
|
|
522
589
|
function keepLatestBlocks(blocks) {
|
|
523
590
|
return blocks.length <= MAX_BLOCKS ? blocks : blocks.slice(-MAX_BLOCKS);
|
|
524
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* keepTail + 截断标记:被去头的存储内容以前缀如实标注(信息保真红线:禁止静默丢弃)。
|
|
594
|
+
* 标记计入 maxChars 预算(存储总量仍 ≤ maxChars,保持既有长度不变量)。
|
|
595
|
+
*/
|
|
596
|
+
function keepTailMarked(value, maxChars) {
|
|
597
|
+
if (value.length <= maxChars)
|
|
598
|
+
return value;
|
|
599
|
+
// marker 计入 maxChars 预算(总量不变量)。省略数 = value.length - kept.length,
|
|
600
|
+
// 而 kept 长度取决于 marker 长度、marker 长度又取决于省略数的位数——
|
|
601
|
+
// 不动点迭代(marker 固定部分 12 字符,位数至多几次即收敛)。
|
|
602
|
+
let dropped = value.length - maxChars + 12;
|
|
603
|
+
for (;;) {
|
|
604
|
+
const next = value.length - maxChars + 12 + String(dropped).length;
|
|
605
|
+
if (next === dropped)
|
|
606
|
+
break;
|
|
607
|
+
dropped = next;
|
|
608
|
+
}
|
|
609
|
+
const marker = `…(前 ${dropped} 字符已省略)\n`;
|
|
610
|
+
const kept = keepTail(value, Math.max(0, maxChars - marker.length));
|
|
611
|
+
return marker + kept;
|
|
612
|
+
}
|
|
525
613
|
function truncateDetail(value) {
|
|
526
614
|
return value.length <= MAX_TOOL_DETAIL_CHARS
|
|
527
615
|
? value
|
|
@@ -22,15 +22,6 @@ export declare const FEISHU_MAX_TABLES = 5;
|
|
|
22
22
|
* 共用,避免三处各自定义后数值漂移。
|
|
23
23
|
*/
|
|
24
24
|
export declare const CARD_BUDGET_BYTES = 28000;
|
|
25
|
-
/**
|
|
26
|
-
* Max byte budget for a single session `tool_result` event's content when
|
|
27
|
-
* replaying an opencode session (L2 pre-fold). The static card budget enforcer
|
|
28
|
-
* (`enforceCardBudget`, maxPanelContentBytes = 2000) would truncate it again at
|
|
29
|
-
* render time anyway; pre-folding here bounds the in-memory `events[]` array
|
|
30
|
-
* and the intermediate card JSON so a single pathological tool_result (e.g. a
|
|
31
|
-
* 500KB file listing) never inflates the replay payload to megabytes.
|
|
32
|
-
*/
|
|
33
|
-
export declare const TOOL_RESULT_MAX_BYTES = 4000;
|
|
34
25
|
/**
|
|
35
26
|
* Count the number of markdown tables in a string.
|
|
36
27
|
*
|
|
@@ -22,15 +22,6 @@ export const FEISHU_MAX_TABLES = 5;
|
|
|
22
22
|
* 共用,避免三处各自定义后数值漂移。
|
|
23
23
|
*/
|
|
24
24
|
export const CARD_BUDGET_BYTES = 28_000;
|
|
25
|
-
/**
|
|
26
|
-
* Max byte budget for a single session `tool_result` event's content when
|
|
27
|
-
* replaying an opencode session (L2 pre-fold). The static card budget enforcer
|
|
28
|
-
* (`enforceCardBudget`, maxPanelContentBytes = 2000) would truncate it again at
|
|
29
|
-
* render time anyway; pre-folding here bounds the in-memory `events[]` array
|
|
30
|
-
* and the intermediate card JSON so a single pathological tool_result (e.g. a
|
|
31
|
-
* 500KB file listing) never inflates the replay payload to megabytes.
|
|
32
|
-
*/
|
|
33
|
-
export const TOOL_RESULT_MAX_BYTES = 4000;
|
|
34
25
|
/**
|
|
35
26
|
* Count the number of markdown tables in a string.
|
|
36
27
|
*
|
|
@@ -20,6 +20,9 @@ export declare function toolHeaderText(tool: ToolEntry): string;
|
|
|
20
20
|
/**
|
|
21
21
|
* Structured body markdown for a tool call. Renders input fields by tool name
|
|
22
22
|
* (Bash → command, Read/Edit/Write → file_path, etc.) and output in a code
|
|
23
|
-
* block.
|
|
23
|
+
* block. An explicit `toolHint` (declared by the runner, 信息保真 C5) selects
|
|
24
|
+
* the same rendering by family instead of name. There is no raw-JSON fallback
|
|
25
|
+
* for unknown tools: `default` returns '' and unknown tools only surface their
|
|
26
|
+
* args through the panel header's `summarizeInput` fallback.
|
|
24
27
|
*/
|
|
25
28
|
export declare function toolBodyMd(tool: ToolEntry): string;
|
package/dist/card/tool-render.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* under formatting noise. `toolHeaderText` produces a compact one-liner header
|
|
10
10
|
* like `✅ **Bash** — pwd` so collapsed panels stay informative.
|
|
11
11
|
*/
|
|
12
|
+
import { truncate } from '../common/truncate.js';
|
|
12
13
|
const HEADER_SUMMARY_MAX = 80;
|
|
13
14
|
const BODY_FIELD_MAX = 600;
|
|
14
15
|
const OUTPUT_MAX = 1200;
|
|
@@ -31,12 +32,25 @@ export function toolHeaderText(tool) {
|
|
|
31
32
|
// P3-6: use the cached parsed record when available (avoids per-render parse).
|
|
32
33
|
const input = tool.parsedInput !== undefined ? tool.parsedInput : tool.input;
|
|
33
34
|
const summary = summarizeInput(tool.name, input);
|
|
34
|
-
|
|
35
|
+
// 信息保真 C3.1:ACP kind 映射后 name 与原始 title 不同时,title 以
|
|
36
|
+
// ` — {summary}` 追加在标题末尾(截断沿用 HEADER_SUMMARY_MAX)。
|
|
37
|
+
// 用 truncate(suffix 为空)而非裸 slice:裸 slice 按 UTF-16 码元切,
|
|
38
|
+
// 切点落在 emoji 代理对中间会留下孤立代理项(P2-31 同族)。
|
|
39
|
+
const blockSummary = tool.summary
|
|
40
|
+
? truncate(tool.summary.replace(/\s+/g, ' ').trim(), HEADER_SUMMARY_MAX, { suffix: '' })
|
|
41
|
+
: '';
|
|
42
|
+
const parts = [summary, blockSummary].filter(Boolean);
|
|
43
|
+
return parts.length > 0
|
|
44
|
+
? `${icon} **${tool.name}** — ${parts.join(' — ')}`
|
|
45
|
+
: `${icon} **${tool.name}**`;
|
|
35
46
|
}
|
|
36
47
|
/**
|
|
37
48
|
* Structured body markdown for a tool call. Renders input fields by tool name
|
|
38
49
|
* (Bash → command, Read/Edit/Write → file_path, etc.) and output in a code
|
|
39
|
-
* block.
|
|
50
|
+
* block. An explicit `toolHint` (declared by the runner, 信息保真 C5) selects
|
|
51
|
+
* the same rendering by family instead of name. There is no raw-JSON fallback
|
|
52
|
+
* for unknown tools: `default` returns '' and unknown tools only surface their
|
|
53
|
+
* args through the panel header's `summarizeInput` fallback.
|
|
40
54
|
*/
|
|
41
55
|
export function toolBodyMd(tool) {
|
|
42
56
|
const parts = [];
|
|
@@ -58,7 +72,8 @@ export function toolBodyMd(tool) {
|
|
|
58
72
|
const body = parts.join('\n\n');
|
|
59
73
|
if (body.length <= BODY_TOTAL_MAX)
|
|
60
74
|
return body;
|
|
61
|
-
|
|
75
|
+
// 同上:走 truncate 保证切点不落在代理对中间(末尾 '…' 由 truncate 附加以对齐预算)。
|
|
76
|
+
return `${truncate(body, BODY_TOTAL_MAX)}\n\n_(body 已截断,完整内容查日志)_`;
|
|
62
77
|
}
|
|
63
78
|
function summarizeInput(name, input) {
|
|
64
79
|
const rec = asRecord(input);
|
|
@@ -105,6 +120,37 @@ function summarizeInput(name, input) {
|
|
|
105
120
|
return pick('command') || pick('file_path') || pick('path') || pick('query');
|
|
106
121
|
}
|
|
107
122
|
}
|
|
123
|
+
// --- Named render bodies (shared by tool-name cases and toolHint branches) ---
|
|
124
|
+
function renderBashInput(str) {
|
|
125
|
+
const cmd = str('command');
|
|
126
|
+
return cmd ? `**Command**\n\`\`\`bash\n${truncate(cmd, BODY_FIELD_MAX)}\n\`\`\`` : '';
|
|
127
|
+
}
|
|
128
|
+
function renderFileInput(str) {
|
|
129
|
+
const fp = str('file_path') || str('path');
|
|
130
|
+
return fp ? `**File** \`${fp}\`` : '';
|
|
131
|
+
}
|
|
132
|
+
function renderGrepInput(str) {
|
|
133
|
+
const lines = [];
|
|
134
|
+
if (str('pattern'))
|
|
135
|
+
lines.push(`**Pattern** \`${str('pattern')}\``);
|
|
136
|
+
if (str('path'))
|
|
137
|
+
lines.push(`**Path** \`${str('path')}\``);
|
|
138
|
+
return lines.join('\n');
|
|
139
|
+
}
|
|
140
|
+
function renderGlobInput(str) {
|
|
141
|
+
const p = str('pattern');
|
|
142
|
+
return p ? `**Pattern** \`${truncate(p, BODY_FIELD_MAX)}\`` : '';
|
|
143
|
+
}
|
|
144
|
+
function renderLsInput(str) {
|
|
145
|
+
const p = str('path');
|
|
146
|
+
return p ? `**Path** \`${p}\`` : '';
|
|
147
|
+
}
|
|
148
|
+
function renderWebFetchInput(str) {
|
|
149
|
+
return str('url') ? `**URL** ${str('url')}` : '';
|
|
150
|
+
}
|
|
151
|
+
function renderWebSearchInput(str) {
|
|
152
|
+
return str('query') ? `**Query** \`${truncate(str('query'), BODY_FIELD_MAX)}\`` : '';
|
|
153
|
+
}
|
|
108
154
|
function renderInput(tool) {
|
|
109
155
|
// P3-6: use the cached parsed record when available (avoids per-render parse).
|
|
110
156
|
const input = tool.parsedInput !== undefined ? tool.parsedInput : tool.input;
|
|
@@ -112,44 +158,45 @@ function renderInput(tool) {
|
|
|
112
158
|
if (!rec)
|
|
113
159
|
return '';
|
|
114
160
|
const str = (k) => (typeof rec[k] === 'string' ? rec[k] : '');
|
|
161
|
+
// 信息保真 C5:hint 优先(runner 自愿声明,只消费不推断);缺失时与现状
|
|
162
|
+
// 完全一致。hint 分支与 name case 共用同一渲染函数,无复制粘贴。
|
|
163
|
+
switch (tool.toolHint) {
|
|
164
|
+
case 'shell':
|
|
165
|
+
return renderBashInput(str);
|
|
166
|
+
case 'file':
|
|
167
|
+
case 'patch':
|
|
168
|
+
return renderFileInput(str);
|
|
169
|
+
case 'search':
|
|
170
|
+
return renderGrepInput(str);
|
|
171
|
+
case 'web':
|
|
172
|
+
return renderWebFetchInput(str) || renderWebSearchInput(str);
|
|
173
|
+
default:
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
115
176
|
switch (tool.name) {
|
|
116
177
|
case 'Bash':
|
|
117
|
-
case 'bash':
|
|
118
|
-
|
|
119
|
-
return cmd ? `**Command**\n\`\`\`bash\n${truncate(cmd, BODY_FIELD_MAX)}\n\`\`\`` : '';
|
|
120
|
-
}
|
|
178
|
+
case 'bash':
|
|
179
|
+
return renderBashInput(str);
|
|
121
180
|
case 'Read':
|
|
122
181
|
case 'Edit':
|
|
123
182
|
case 'Write':
|
|
124
183
|
case 'NotebookEdit':
|
|
125
184
|
case 'read':
|
|
126
185
|
case 'edit':
|
|
127
|
-
case 'write':
|
|
128
|
-
|
|
129
|
-
return fp ? `**File** \`${fp}\`` : '';
|
|
130
|
-
}
|
|
186
|
+
case 'write':
|
|
187
|
+
return renderFileInput(str);
|
|
131
188
|
case 'Grep':
|
|
132
|
-
case 'grep':
|
|
133
|
-
|
|
134
|
-
if (str('pattern'))
|
|
135
|
-
lines.push(`**Pattern** \`${str('pattern')}\``);
|
|
136
|
-
if (str('path'))
|
|
137
|
-
lines.push(`**Path** \`${str('path')}\``);
|
|
138
|
-
return lines.join('\n');
|
|
139
|
-
}
|
|
189
|
+
case 'grep':
|
|
190
|
+
return renderGrepInput(str);
|
|
140
191
|
case 'Glob':
|
|
141
|
-
case 'find':
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
case 'ls': {
|
|
146
|
-
const p = str('path');
|
|
147
|
-
return p ? `**Path** \`${p}\`` : '';
|
|
148
|
-
}
|
|
192
|
+
case 'find':
|
|
193
|
+
return renderGlobInput(str);
|
|
194
|
+
case 'ls':
|
|
195
|
+
return renderLsInput(str);
|
|
149
196
|
case 'WebFetch':
|
|
150
|
-
return
|
|
197
|
+
return renderWebFetchInput(str);
|
|
151
198
|
case 'WebSearch':
|
|
152
|
-
return
|
|
199
|
+
return renderWebSearchInput(str);
|
|
153
200
|
default:
|
|
154
201
|
return '';
|
|
155
202
|
}
|
|
@@ -170,7 +217,4 @@ function asRecord(input) {
|
|
|
170
217
|
}
|
|
171
218
|
return null;
|
|
172
219
|
}
|
|
173
|
-
function truncate(s, max) {
|
|
174
|
-
return s.length > max ? `${s.slice(0, max)}…` : s;
|
|
175
|
-
}
|
|
176
220
|
//# sourceMappingURL=tool-render.js.map
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ export function decideRuntime(isBun, probe) {
|
|
|
14
14
|
/** Check whether `bun` is present and runs `--version` successfully. */
|
|
15
15
|
function probeBun() {
|
|
16
16
|
try {
|
|
17
|
-
const res = spawnSync('bun', ['--version'], { stdio: 'ignore' });
|
|
17
|
+
const res = spawnSync('bun', ['--version'], { stdio: 'ignore', windowsHide: true });
|
|
18
18
|
return { error: res.error !== undefined, status: res.status };
|
|
19
19
|
}
|
|
20
20
|
catch {
|
|
@@ -75,7 +75,7 @@ function main() {
|
|
|
75
75
|
isBun: process.isBun ?? false,
|
|
76
76
|
probe: probeBun(),
|
|
77
77
|
importEntry: () => import(entryUrl.href),
|
|
78
|
-
spawnBun: (e, a) => spawn('bun', [e, ...a], { stdio: 'inherit' }),
|
|
78
|
+
spawnBun: (e, a) => spawn('bun', [e, ...a], { stdio: 'inherit', windowsHide: true }),
|
|
79
79
|
onSignal: (sig, handler) => process.on(sig, handler),
|
|
80
80
|
offSignal: (sig, handler) => process.off(sig, handler),
|
|
81
81
|
killSelf: (sig) => process.kill(process.pid, sig),
|
package/dist/common/fs.d.ts
CHANGED
|
@@ -6,3 +6,17 @@
|
|
|
6
6
|
* an error worth propagating.
|
|
7
7
|
*/
|
|
8
8
|
export declare function silentlyUnlink(path: string): void;
|
|
9
|
+
export declare function isBusyError(err: unknown): boolean;
|
|
10
|
+
export interface BusyRetryOptions {
|
|
11
|
+
/** 平台注入(测试用);默认当前宿主。占用重试仅 win32 生效 */
|
|
12
|
+
platform?: NodeJS.Platform;
|
|
13
|
+
/** 重试耗尽(或 posix 首次占用即失败)时回调,用于告警/保留现场 */
|
|
14
|
+
onGiveUp?: (err: unknown) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 对 sync 文件操作套占用退避重试:win32 上遇 EPERM/EBUSY 按退避序列重试,
|
|
18
|
+
* 其他错误或重试耗尽后抛最后一个错误;posix 上占用错误不重试、立即失败。
|
|
19
|
+
* `onGiveUp` 仅在「占用导致的放弃」时回调(告警/保留现场);非占用错误
|
|
20
|
+
* (如 EXDEV)不走回调、直接透传,避免误触发调用方的占用善后逻辑。
|
|
21
|
+
*/
|
|
22
|
+
export declare function withBusyRetry<T>(op: () => T, opts?: BusyRetryOptions): T;
|