@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.
Files changed (31) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/apps/runner/src/engines/claudeCodeRunner.js +71 -9
  3. package/apps/runner/src/engines/claudeCodeRunner.test.js +189 -1
  4. package/apps/runner/src/engines/index.js +2 -0
  5. package/apps/runner/src/engines/kimiCodeRunner.js +584 -0
  6. package/apps/runner/src/engines/kimiCodeRunner.test.js +127 -0
  7. package/apps/runner/src/engines/openCodeRunner.test.js +164 -1
  8. package/apps/{server/src/agents → runner/src/engines}/runnerContract.test.js +67 -0
  9. package/apps/runner/src/runManager.js +6 -1
  10. package/apps/runner/src/runManager.test.js +1 -1
  11. package/apps/server/src/agentSessionDiscovery.js +136 -0
  12. package/apps/server/src/agentSessionDiscovery.test.js +59 -0
  13. package/apps/server/src/agents/claudeCodeRunner.js +1 -839
  14. package/apps/server/src/agents/index.js +2 -0
  15. package/apps/server/src/agents/kimiCodeRunner.js +14 -0
  16. package/apps/server/src/agents/openCodeRunner.js +1 -636
  17. package/apps/server/src/codexSessions.js +1 -1
  18. package/apps/server/src/runDispatchService.js +14 -2
  19. package/apps/web/dist/assets/{CodexSessionManagerDialog-B_F9ZWKy.js → CodexSessionManagerDialog-Bq8GNsSY.js} +1 -1
  20. package/apps/web/dist/assets/{TaskDiffReviewDialog-CPqGk_q2.js → TaskDiffReviewDialog-pec7Va_O.js} +2 -2
  21. package/apps/web/dist/assets/{WorkbenchSettingsDialog-CWl81vlG.js → WorkbenchSettingsDialog-Bzk1BUM2.js} +1 -1
  22. package/apps/web/dist/assets/WorkbenchView-BYtJENv9.js +60 -0
  23. package/apps/web/dist/assets/index-DW5iQdjP.js +2 -0
  24. package/apps/web/dist/index.html +1 -1
  25. package/package.json +1 -1
  26. package/packages/shared/src/index.js +6 -0
  27. package/scripts/doctor.mjs +8 -0
  28. package/apps/server/src/agents/claudeCodeRunner.test.js +0 -433
  29. package/apps/server/src/agents/openCodeRunner.test.js +0 -236
  30. package/apps/web/dist/assets/WorkbenchView-gbRu02Lv.js +0 -60
  31. 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
- reject(normalizeSpawnError(error))
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
- reject(new Error(detail))
941
+ settleError(new Error(detail))
874
942
  return
875
943
  }
876
944
 
877
- onEvent(createCompletedEnvelopeEvent(finalMessage))
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 { createClaudeNormalizationState, normalizeClaudeEvents } from './claudeCodeRunner.js'
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