@muyichengshayu/promptx 0.2.8 → 0.2.10
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 +15 -0
- package/apps/runner/src/engines/claudeCodeRunner.js +71 -9
- package/apps/runner/src/engines/claudeCodeRunner.test.js +189 -1
- package/apps/runner/src/engines/index.js +2 -0
- package/apps/runner/src/engines/kimiCodeRunner.js +584 -0
- package/apps/runner/src/engines/kimiCodeRunner.test.js +127 -0
- package/apps/runner/src/engines/openCodeRunner.test.js +164 -1
- package/apps/{server/src/agents → runner/src/engines}/runnerContract.test.js +67 -0
- package/apps/runner/src/runManager.js +6 -1
- package/apps/runner/src/runManager.test.js +1 -1
- package/apps/server/src/agentSessionDiscovery.js +136 -0
- package/apps/server/src/agentSessionDiscovery.test.js +59 -0
- package/apps/server/src/agents/claudeCodeRunner.js +1 -839
- package/apps/server/src/agents/index.js +2 -0
- package/apps/server/src/agents/kimiCodeRunner.js +14 -0
- package/apps/server/src/agents/openCodeRunner.js +1 -636
- package/apps/server/src/codexSessions.js +1 -1
- package/apps/server/src/runDispatchService.js +14 -2
- package/apps/web/dist/assets/{CodexSessionManagerDialog-B_F9ZWKy.js → CodexSessionManagerDialog-Bq8GNsSY.js} +1 -1
- package/apps/web/dist/assets/{TaskDiffReviewDialog-CPqGk_q2.js → TaskDiffReviewDialog-pec7Va_O.js} +2 -2
- package/apps/web/dist/assets/{WorkbenchSettingsDialog-CWl81vlG.js → WorkbenchSettingsDialog-Bzk1BUM2.js} +1 -1
- package/apps/web/dist/assets/WorkbenchView-BYtJENv9.js +60 -0
- package/apps/web/dist/assets/index-DW5iQdjP.js +2 -0
- package/apps/web/dist/index.html +1 -1
- package/package.json +1 -1
- package/packages/shared/src/index.js +6 -0
- package/scripts/doctor.mjs +8 -0
- package/apps/server/src/agents/claudeCodeRunner.test.js +0 -433
- package/apps/server/src/agents/openCodeRunner.test.js +0 -236
- package/apps/web/dist/assets/WorkbenchView-gbRu02Lv.js +0 -60
- package/apps/web/dist/assets/index-5LxHpYf5.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.10
|
|
4
|
+
|
|
5
|
+
- 修复 Claude Code 结果完成时偶发卡死的问题:当 Claude CLI 在 result 事件后延迟退出时,runner 会进入优雅退出等待,避免强制 kill 导致消息丢失。
|
|
6
|
+
- 完善 Kimi Code 执行过程展示:思考过程(thinking)不再被隐藏,可直接在执行面板中查看;工具调用(ReadFile / WriteFile / Shell 等)正确映射到前端分类(读取/写入/命令),便于分组展示。
|
|
7
|
+
- 补齐 Kimi Code 事件流完整性:新增 thread.started 事件发送,修复会话创建元信息泄漏到过程日志的问题。
|
|
8
|
+
- 健壮化 Kimi Code 参数解析:支持 arguments 既为字符串也可为对象,避免空参数展示为 `{}`。
|
|
9
|
+
- 代码变更弹窗轮次选择器日期精确到秒:select option 中显示完整时分秒,方便在密集 run 中快速定位目标轮次。
|
|
10
|
+
|
|
11
|
+
## 0.2.9
|
|
12
|
+
|
|
13
|
+
- 接入 `Kimi Code CLI` 执行引擎:工作台 Agent 选择器新增 Kimi Code 选项,支持本地会话发现、线程复用、TodoList 过程展示与停止控制,与现有 Codex / Claude Code / OpenCode 并列为第四类 Agent。
|
|
14
|
+
- 工作台 Agent 选择器与过滤状态持久化:切换任务后会自动恢复上次选中的 Agent 引擎和中栏过滤条件,不再每次回到默认值。
|
|
15
|
+
- runner 状态提示支持多语言:idle 进度提示(排队中、运行中、停止中)通过 `messageKey` 机制走前端 i18n 翻译,英文环境下显示对应英文文案。
|
|
16
|
+
- 修复图片上传地址在云主机部署下失效的问题:前端 `API_BASE` 改为运行时按 `window.location.origin` 计算,数据库中只存相对路径;同时兼容已存的老数据(`localhost` / `127.0.0.1` 绝对路径在显示时自动替换为当前 host),runner 收到的 prompt 仍会正确转换为本地地址。
|
|
17
|
+
|
|
3
18
|
## 0.2.8
|
|
4
19
|
|
|
5
20
|
- 修复 `Claude Code` 的 `TodoWrite` 过程映射:待办列表会被归一为结构化 `todo_list` 事件,正确展示待办内容、进行中与已完成状态,不再只暴露原始工具输入。
|
|
@@ -21,6 +21,14 @@ import { createManagedSpawnOptions, forceStopChildProcess } from '../processCont
|
|
|
21
21
|
const CLAUDE_CODE_BIN = process.env.CLAUDE_CODE_BIN || 'claude'
|
|
22
22
|
const CLAUDE_DEFAULT_ARGS = ['--dangerously-skip-permissions']
|
|
23
23
|
const RESOLVED_CLAUDE_CODE_BIN = resolveClaudeCodeBinary()
|
|
24
|
+
const CLAUDE_RESULT_EXIT_GRACE_MS = Math.max(
|
|
25
|
+
0,
|
|
26
|
+
Number(process.env.PROMPTX_CLAUDE_RESULT_EXIT_GRACE_MS) || 3000
|
|
27
|
+
)
|
|
28
|
+
const CLAUDE_RESULT_FORCE_STOP_GRACE_MS = Math.max(
|
|
29
|
+
200,
|
|
30
|
+
Number(process.env.PROMPTX_CLAUDE_RESULT_FORCE_STOP_GRACE_MS) || 1000
|
|
31
|
+
)
|
|
24
32
|
|
|
25
33
|
function resolveClaudeCodeBinary() {
|
|
26
34
|
if (process.platform !== 'win32') {
|
|
@@ -798,8 +806,60 @@ export function streamPromptToClaudeCodeSession(sessionInput, prompt, callbacks
|
|
|
798
806
|
let finalSessionId = String(session.engineSessionId || session.engineThreadId || session.codexThreadId || '').trim()
|
|
799
807
|
let fatalClaudeErrorMessage = ''
|
|
800
808
|
let fatalClaudeErrorTriggered = false
|
|
809
|
+
let resultExitGraceTimer = null
|
|
810
|
+
let settled = false
|
|
811
|
+
let resolveResult = null
|
|
812
|
+
let rejectResult = null
|
|
801
813
|
const normalizationState = createClaudeNormalizationState()
|
|
802
814
|
|
|
815
|
+
const clearResultExitGraceTimer = () => {
|
|
816
|
+
if (resultExitGraceTimer) {
|
|
817
|
+
clearTimeout(resultExitGraceTimer)
|
|
818
|
+
resultExitGraceTimer = null
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const settleCompleted = (options = {}) => {
|
|
823
|
+
if (settled) {
|
|
824
|
+
return
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
settled = true
|
|
828
|
+
clearResultExitGraceTimer()
|
|
829
|
+
onEvent(createCompletedEnvelopeEvent(finalMessage))
|
|
830
|
+
resolveResult?.({
|
|
831
|
+
sessionId: session.id,
|
|
832
|
+
threadId: finalSessionId,
|
|
833
|
+
message: finalMessage,
|
|
834
|
+
})
|
|
835
|
+
|
|
836
|
+
if (options.stopChild && child.exitCode === null && child.signalCode === null) {
|
|
837
|
+
forceStopChildProcess(child, { graceMs: CLAUDE_RESULT_FORCE_STOP_GRACE_MS })
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const settleError = (error) => {
|
|
842
|
+
if (settled) {
|
|
843
|
+
return
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
settled = true
|
|
847
|
+
clearResultExitGraceTimer()
|
|
848
|
+
rejectResult?.(error)
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const scheduleResultExitGrace = () => {
|
|
852
|
+
if (settled || resultExitGraceTimer || CLAUDE_RESULT_EXIT_GRACE_MS <= 0) {
|
|
853
|
+
return
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
resultExitGraceTimer = setTimeout(() => {
|
|
857
|
+
resultExitGraceTimer = null
|
|
858
|
+
settleCompleted({ stopChild: true })
|
|
859
|
+
}, CLAUDE_RESULT_EXIT_GRACE_MS)
|
|
860
|
+
resultExitGraceTimer.unref?.()
|
|
861
|
+
}
|
|
862
|
+
|
|
803
863
|
const rememberSessionId = (sessionId) => {
|
|
804
864
|
const value = String(sessionId || '').trim()
|
|
805
865
|
if (!value || value === finalSessionId) {
|
|
@@ -836,6 +896,7 @@ export function streamPromptToClaudeCodeSession(sessionInput, prompt, callbacks
|
|
|
836
896
|
|
|
837
897
|
if (String(event?.type || '').trim().toLowerCase() === 'result') {
|
|
838
898
|
finalMessage = extractClaudeResultText(event) || finalMessage
|
|
899
|
+
scheduleResultExitGrace()
|
|
839
900
|
}
|
|
840
901
|
}
|
|
841
902
|
|
|
@@ -857,11 +918,18 @@ export function streamPromptToClaudeCodeSession(sessionInput, prompt, callbacks
|
|
|
857
918
|
})
|
|
858
919
|
|
|
859
920
|
const result = new Promise((resolve, reject) => {
|
|
921
|
+
resolveResult = resolve
|
|
922
|
+
rejectResult = reject
|
|
923
|
+
|
|
860
924
|
child.on('error', (error) => {
|
|
861
|
-
|
|
925
|
+
settleError(normalizeSpawnError(error))
|
|
862
926
|
})
|
|
863
927
|
|
|
864
928
|
child.on('close', (code) => {
|
|
929
|
+
if (settled) {
|
|
930
|
+
return
|
|
931
|
+
}
|
|
932
|
+
|
|
865
933
|
flushBufferedText(stdoutBuffer).forEach(emitClaudeJsonLine)
|
|
866
934
|
flushBufferedText(stderrBuffer).forEach((line) => {
|
|
867
935
|
lastStderrLine = line
|
|
@@ -870,17 +938,11 @@ export function streamPromptToClaudeCodeSession(sessionInput, prompt, callbacks
|
|
|
870
938
|
|
|
871
939
|
if (code !== 0) {
|
|
872
940
|
const detail = fatalClaudeErrorMessage || lastStderrLine || 'Claude Code 执行失败。'
|
|
873
|
-
|
|
941
|
+
settleError(new Error(detail))
|
|
874
942
|
return
|
|
875
943
|
}
|
|
876
944
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
resolve({
|
|
880
|
-
sessionId: session.id,
|
|
881
|
-
threadId: finalSessionId,
|
|
882
|
-
message: finalMessage,
|
|
883
|
-
})
|
|
945
|
+
settleCompleted()
|
|
884
946
|
})
|
|
885
947
|
})
|
|
886
948
|
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import test from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createClaudeNormalizationState,
|
|
6
|
+
extractClaudeAssistantText,
|
|
7
|
+
extractClaudeResultText,
|
|
8
|
+
extractClaudeSessionId,
|
|
9
|
+
normalizeClaudeEvent,
|
|
10
|
+
normalizeClaudeEvents,
|
|
11
|
+
} from './claudeCodeRunner.js'
|
|
5
12
|
|
|
6
13
|
test('runner claudeCodeRunner maps fatal auth api_retry to error event', () => {
|
|
7
14
|
assert.deepEqual(
|
|
@@ -277,3 +284,184 @@ test('runner claudeCodeRunner maps TodoWrite into todo_list events', () => {
|
|
|
277
284
|
}]
|
|
278
285
|
)
|
|
279
286
|
})
|
|
287
|
+
|
|
288
|
+
test('extractClaudeAssistantText joins nested text parts', () => {
|
|
289
|
+
const text = extractClaudeAssistantText({
|
|
290
|
+
type: 'assistant',
|
|
291
|
+
message: {
|
|
292
|
+
content: [
|
|
293
|
+
{ type: 'text', text: '第一段' },
|
|
294
|
+
{ type: 'text', text: '第二段' },
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
assert.equal(text, '第一段\n第二段')
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
test('extractClaudeSessionId reads common session id fields', () => {
|
|
303
|
+
assert.equal(extractClaudeSessionId({ session_id: 'claude-session-1' }), 'claude-session-1')
|
|
304
|
+
assert.equal(extractClaudeSessionId({ result: { session_id: 'claude-session-2' } }), 'claude-session-2')
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
test('normalizeClaudeEvent maps assistant output to agent message', () => {
|
|
308
|
+
assert.deepEqual(
|
|
309
|
+
normalizeClaudeEvent({
|
|
310
|
+
type: 'assistant',
|
|
311
|
+
message: {
|
|
312
|
+
content: [{ type: 'text', text: '已完成修改' }],
|
|
313
|
+
},
|
|
314
|
+
}),
|
|
315
|
+
{
|
|
316
|
+
type: 'item.completed',
|
|
317
|
+
item: {
|
|
318
|
+
type: 'agent_message',
|
|
319
|
+
text: '已完成修改',
|
|
320
|
+
},
|
|
321
|
+
}
|
|
322
|
+
)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
test('normalizeClaudeEvent maps result output to turn completion', () => {
|
|
326
|
+
assert.deepEqual(
|
|
327
|
+
normalizeClaudeEvent({
|
|
328
|
+
type: 'result',
|
|
329
|
+
result: '最终回复',
|
|
330
|
+
}),
|
|
331
|
+
{
|
|
332
|
+
type: 'turn.completed',
|
|
333
|
+
result: '最终回复',
|
|
334
|
+
}
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
assert.equal(extractClaudeResultText({ result: '最终回复' }), '最终回复')
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test('normalizeClaudeEvents maps system init to thread start', () => {
|
|
341
|
+
assert.deepEqual(
|
|
342
|
+
normalizeClaudeEvents({
|
|
343
|
+
type: 'system',
|
|
344
|
+
subtype: 'init',
|
|
345
|
+
session_id: 'claude-session-init',
|
|
346
|
+
}),
|
|
347
|
+
[{
|
|
348
|
+
type: 'thread.started',
|
|
349
|
+
thread_id: 'claude-session-init',
|
|
350
|
+
}]
|
|
351
|
+
)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
test('normalizeClaudeEvents maps thinking, tool use and text blocks', () => {
|
|
355
|
+
const state = createClaudeNormalizationState()
|
|
356
|
+
|
|
357
|
+
assert.deepEqual(
|
|
358
|
+
normalizeClaudeEvents({
|
|
359
|
+
type: 'assistant',
|
|
360
|
+
message: {
|
|
361
|
+
content: [
|
|
362
|
+
{ type: 'thinking', thinking: '先看看目录结构' },
|
|
363
|
+
{ type: 'tool_use', id: 'tool-1', name: 'Bash', input: { command: 'ls -1' } },
|
|
364
|
+
{ type: 'text', text: '已查看完成' },
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
}, state),
|
|
368
|
+
[
|
|
369
|
+
{
|
|
370
|
+
type: 'item.started',
|
|
371
|
+
item: {
|
|
372
|
+
type: 'reasoning',
|
|
373
|
+
text: '先看看目录结构',
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
type: 'item.started',
|
|
378
|
+
item: {
|
|
379
|
+
type: 'command_execution',
|
|
380
|
+
command: 'Bash: ls -1',
|
|
381
|
+
status: 'in_progress',
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
type: 'item.completed',
|
|
386
|
+
item: {
|
|
387
|
+
type: 'agent_message',
|
|
388
|
+
text: '已查看完成',
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
]
|
|
392
|
+
)
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
test('normalizeClaudeEvents maps tool results back to remembered tool call', () => {
|
|
396
|
+
const state = createClaudeNormalizationState()
|
|
397
|
+
normalizeClaudeEvents({
|
|
398
|
+
type: 'assistant',
|
|
399
|
+
message: {
|
|
400
|
+
content: [
|
|
401
|
+
{ type: 'tool_use', id: 'tool-2', name: 'Bash', input: { command: 'pwd' } },
|
|
402
|
+
],
|
|
403
|
+
},
|
|
404
|
+
}, state)
|
|
405
|
+
|
|
406
|
+
assert.deepEqual(
|
|
407
|
+
normalizeClaudeEvents({
|
|
408
|
+
type: 'user',
|
|
409
|
+
message: {
|
|
410
|
+
content: [
|
|
411
|
+
{ type: 'tool_result', tool_use_id: 'tool-2', content: '/tmp/demo', is_error: false },
|
|
412
|
+
],
|
|
413
|
+
},
|
|
414
|
+
}, state),
|
|
415
|
+
[{
|
|
416
|
+
type: 'item.completed',
|
|
417
|
+
item: {
|
|
418
|
+
type: 'command_execution',
|
|
419
|
+
command: 'Bash: pwd',
|
|
420
|
+
status: 'completed',
|
|
421
|
+
exit_code: 0,
|
|
422
|
+
aggregated_output: '/tmp/demo',
|
|
423
|
+
},
|
|
424
|
+
}]
|
|
425
|
+
)
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
test('normalizeClaudeEvents stringifies structured tool results', () => {
|
|
429
|
+
const state = createClaudeNormalizationState()
|
|
430
|
+
normalizeClaudeEvents({
|
|
431
|
+
type: 'assistant',
|
|
432
|
+
message: {
|
|
433
|
+
content: [
|
|
434
|
+
{ type: 'tool_use', id: 'tool-3', name: 'Read', input: { file_path: '/tmp/demo.txt' } },
|
|
435
|
+
],
|
|
436
|
+
},
|
|
437
|
+
}, state)
|
|
438
|
+
|
|
439
|
+
assert.deepEqual(
|
|
440
|
+
normalizeClaudeEvents({
|
|
441
|
+
type: 'user',
|
|
442
|
+
message: {
|
|
443
|
+
content: [
|
|
444
|
+
{
|
|
445
|
+
type: 'tool_result',
|
|
446
|
+
tool_use_id: 'tool-3',
|
|
447
|
+
content: [
|
|
448
|
+
{ type: 'text', text: '<path>/tmp/demo.txt</path>' },
|
|
449
|
+
{ type: 'text', text: '<type>file</type>' },
|
|
450
|
+
],
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
state),
|
|
456
|
+
[{
|
|
457
|
+
type: 'item.completed',
|
|
458
|
+
item: {
|
|
459
|
+
type: 'command_execution',
|
|
460
|
+
command: 'Read: /tmp/demo.txt',
|
|
461
|
+
status: 'completed',
|
|
462
|
+
exit_code: 0,
|
|
463
|
+
aggregated_output: '<path>/tmp/demo.txt</path>\n<type>file</type>',
|
|
464
|
+
},
|
|
465
|
+
}]
|
|
466
|
+
)
|
|
467
|
+
})
|
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
import { codexRunner } from './codexRunner.js'
|
|
8
8
|
import { claudeCodeRunner } from './claudeCodeRunner.js'
|
|
9
9
|
import { openCodeRunner } from './openCodeRunner.js'
|
|
10
|
+
import { kimiCodeRunner } from './kimiCodeRunner.js'
|
|
10
11
|
import { shellRunner } from './shellRunner.js'
|
|
11
12
|
|
|
12
13
|
const runnerRegistry = new Map([
|
|
13
14
|
[codexRunner.engine, codexRunner],
|
|
14
15
|
[claudeCodeRunner.engine, claudeCodeRunner],
|
|
15
16
|
[openCodeRunner.engine, openCodeRunner],
|
|
17
|
+
[kimiCodeRunner.engine, kimiCodeRunner],
|
|
16
18
|
[shellRunner.engine, shellRunner],
|
|
17
19
|
])
|
|
18
20
|
|