@muyichengshayu/promptx 0.2.16 → 0.2.18

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 (57) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/apps/runner/src/codex.js +114 -16
  3. package/apps/runner/src/engines/claudeCodeRunner.test.js +467 -0
  4. package/apps/runner/src/engines/kimiCodeRunner.test.js +127 -0
  5. package/apps/runner/src/engines/openCodeRunner.test.js +236 -0
  6. package/apps/runner/src/engines/runnerContract.test.js +510 -0
  7. package/apps/runner/src/engines/shellRunner.test.js +46 -0
  8. package/apps/runner/src/runManager.test.js +913 -0
  9. package/apps/runner/src/serverClient.test.js +93 -0
  10. package/apps/server/src/agentSessionDiscovery.test.js +572 -0
  11. package/apps/server/src/appPaths.test.js +52 -0
  12. package/apps/server/src/assetRoutes.test.js +168 -0
  13. package/apps/server/src/codex.test.js +518 -0
  14. package/apps/server/src/codexRoutes.js +12 -0
  15. package/apps/server/src/codexRoutes.test.js +376 -0
  16. package/apps/server/src/codexRuns.test.js +160 -0
  17. package/apps/server/src/codexSessions.test.js +369 -0
  18. package/apps/server/src/db.test.js +182 -0
  19. package/apps/server/src/gitDiff.test.js +542 -0
  20. package/apps/server/src/gitDiffClient.test.js +140 -0
  21. package/apps/server/src/index.js +2 -0
  22. package/apps/server/src/internalRoutes.test.js +134 -0
  23. package/apps/server/src/maintenance.test.js +154 -0
  24. package/apps/server/src/processControl.test.js +147 -0
  25. package/apps/server/src/relayClient.test.js +478 -0
  26. package/apps/server/src/relayConfig.test.js +73 -0
  27. package/apps/server/src/relayProtocol.test.js +49 -0
  28. package/apps/server/src/relayServer.test.js +798 -0
  29. package/apps/server/src/relayTenants.test.js +137 -0
  30. package/apps/server/src/relayUsageStore.test.js +65 -0
  31. package/apps/server/src/repository.test.js +150 -0
  32. package/apps/server/src/runDispatchService.test.js +563 -0
  33. package/apps/server/src/runEventIngest.test.js +225 -0
  34. package/apps/server/src/runRecovery.test.js +73 -0
  35. package/apps/server/src/runnerClient.test.js +80 -0
  36. package/apps/server/src/runnerDispatch.test.js +136 -0
  37. package/apps/server/src/systemConfig.test.js +112 -0
  38. package/apps/server/src/systemRoutes.test.js +319 -0
  39. package/apps/server/src/taskRoutes.test.js +775 -0
  40. package/apps/server/src/upload.test.js +30 -0
  41. package/apps/server/src/webAppRoutes.test.js +67 -0
  42. package/apps/server/src/workspaceFiles.js +170 -0
  43. package/apps/server/src/workspaceFiles.test.js +327 -0
  44. package/apps/web/dist/assets/{CodexSessionManagerDialog-D1PwOD4T.js → CodexSessionManagerDialog-D_72LIMv.js} +1 -1
  45. package/apps/web/dist/assets/{TaskDiffReviewDialog-GKKv-IkZ.js → TaskDiffReviewDialog-CoQgN73y.js} +1 -1
  46. package/apps/web/dist/assets/{WorkbenchSettingsDialog-IWlkg3kU.js → WorkbenchSettingsDialog-Bw-fcsXi.js} +1 -1
  47. package/apps/web/dist/assets/WorkbenchView-CebqJlAz.css +1 -0
  48. package/apps/web/dist/assets/WorkbenchView-CsZnW4Q7.js +58 -0
  49. package/apps/web/dist/assets/index-Bt9T2IKp.js +2 -0
  50. package/apps/web/dist/assets/{index-BAfqUG7o.css → index-DaJU954y.css} +1 -1
  51. package/apps/web/dist/index.html +2 -2
  52. package/package.json +14 -21
  53. package/packages/shared/src/dailyLogStream.test.js +29 -0
  54. package/packages/shared/src/shellCommands.test.js +45 -0
  55. package/apps/web/dist/assets/WorkbenchView-CK1snPBz.css +0 -1
  56. package/apps/web/dist/assets/WorkbenchView-dXHPTH_M.js +0 -58
  57. package/apps/web/dist/assets/index-DaIoquOV.js +0 -2
@@ -0,0 +1,127 @@
1
+ import assert from 'node:assert/strict'
2
+ import test from 'node:test'
3
+
4
+ import {
5
+ isKimiInfoStderrLine,
6
+ normalizeKimiEvents,
7
+ createKimiNormalizationState,
8
+ } from './kimiCodeRunner.js'
9
+ import { AGENT_RUN_ITEM_TYPES } from '@promptx/shared'
10
+
11
+ test('isKimiInfoStderrLine treats resume hints as non-error metadata', () => {
12
+ assert.equal(
13
+ isKimiInfoStderrLine('To resume this session: kimi -r ffb2f903-1173-4f8a-a49a-c8b0deae3e2d'),
14
+ true
15
+ )
16
+ assert.equal(
17
+ isKimiInfoStderrLine('To resume this session: kimi --session ffb2f903-1173-4f8a-a49a-c8b0deae3e2d'),
18
+ true
19
+ )
20
+ })
21
+
22
+ test('isKimiInfoStderrLine keeps real stderr visible', () => {
23
+ assert.equal(isKimiInfoStderrLine('Error: authentication failed'), false)
24
+ })
25
+
26
+ test('normalizeKimiEvents maps SetTodoList tool_call to todo_list events', () => {
27
+ const state = createKimiNormalizationState()
28
+
29
+ const assistantEvent = {
30
+ role: 'assistant',
31
+ content: [],
32
+ tool_calls: [{
33
+ id: 'call-1',
34
+ function: {
35
+ name: 'SetTodoList',
36
+ arguments: JSON.stringify({
37
+ todos: [
38
+ { title: 'Task A', status: 'in_progress' },
39
+ { title: 'Task B', status: 'done' },
40
+ { title: 'Task C', status: 'pending' },
41
+ ],
42
+ }),
43
+ },
44
+ }],
45
+ }
46
+
47
+ const startedEvents = normalizeKimiEvents(assistantEvent, state)
48
+ assert.equal(startedEvents.length, 2)
49
+ assert.equal(startedEvents[0].type, 'turn.started')
50
+
51
+ const todoStarted = startedEvents[1]
52
+ assert.equal(todoStarted.type, 'item.started')
53
+ assert.equal(todoStarted.item.type, AGENT_RUN_ITEM_TYPES.TODO_LIST)
54
+ assert.equal(todoStarted.item.items.length, 3)
55
+ assert.deepEqual(todoStarted.item.items[0], { text: 'Task A', status: 'in_progress', completed: false })
56
+ assert.deepEqual(todoStarted.item.items[1], { text: 'Task B', status: 'completed', completed: true })
57
+ assert.deepEqual(todoStarted.item.items[2], { text: 'Task C', status: 'pending', completed: false })
58
+
59
+ const toolEvent = {
60
+ role: 'tool',
61
+ tool_call_id: 'call-1',
62
+ content: 'Todo list updated',
63
+ }
64
+
65
+ const completedEvents = normalizeKimiEvents(toolEvent, state)
66
+ assert.equal(completedEvents.length, 1)
67
+ assert.equal(completedEvents[0].type, 'item.completed')
68
+ assert.equal(completedEvents[0].item.type, AGENT_RUN_ITEM_TYPES.TODO_LIST)
69
+ assert.equal(completedEvents[0].item.items.length, 3)
70
+ })
71
+
72
+ test('normalizeKimiEvents keeps regular tool_calls as command_execution', () => {
73
+ const state = createKimiNormalizationState()
74
+
75
+ const assistantEvent = {
76
+ role: 'assistant',
77
+ content: [],
78
+ tool_calls: [{
79
+ id: 'call-2',
80
+ function: {
81
+ name: 'Shell',
82
+ arguments: JSON.stringify({ command: 'ls' }),
83
+ },
84
+ }],
85
+ }
86
+
87
+ const startedEvents = normalizeKimiEvents(assistantEvent, state)
88
+ assert.equal(startedEvents.length, 2)
89
+ assert.equal(startedEvents[0].type, 'turn.started')
90
+
91
+ const cmdStarted = startedEvents[1]
92
+ assert.equal(cmdStarted.type, 'item.started')
93
+ assert.equal(cmdStarted.item.type, AGENT_RUN_ITEM_TYPES.COMMAND_EXECUTION)
94
+
95
+ const toolEvent = {
96
+ role: 'tool',
97
+ tool_call_id: 'call-2',
98
+ content: 'file.txt',
99
+ }
100
+
101
+ const completedEvents = normalizeKimiEvents(toolEvent, state)
102
+ assert.equal(completedEvents.length, 1)
103
+ assert.equal(completedEvents[0].type, 'item.completed')
104
+ assert.equal(completedEvents[0].item.type, AGENT_RUN_ITEM_TYPES.COMMAND_EXECUTION)
105
+ })
106
+
107
+ test('normalizeKimiEvents handles SetTodoList with empty or malformed todos', () => {
108
+ const state = createKimiNormalizationState()
109
+
110
+ const assistantEvent = {
111
+ role: 'assistant',
112
+ content: [],
113
+ tool_calls: [{
114
+ id: 'call-3',
115
+ function: {
116
+ name: 'SetTodoList',
117
+ arguments: JSON.stringify({ todos: [] }),
118
+ },
119
+ }],
120
+ }
121
+
122
+ const events = normalizeKimiEvents(assistantEvent, state)
123
+ assert.equal(events.length, 2)
124
+ assert.equal(events[1].type, 'item.started')
125
+ assert.equal(events[1].item.type, AGENT_RUN_ITEM_TYPES.TODO_LIST)
126
+ assert.equal(events[1].item.items.length, 0)
127
+ })
@@ -0,0 +1,236 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import {
5
+ createOpenCodeNormalizationState,
6
+ extractOpenCodeErrorMessage,
7
+ extractOpenCodeSessionId,
8
+ extractOpenCodeText,
9
+ extractOpenCodeUsage,
10
+ normalizeOpenCodeEvent,
11
+ normalizeOpenCodeEvents,
12
+ } from './openCodeRunner.js'
13
+
14
+ test('runner openCodeRunner maps sub-agent task tool_use to collaboration events', () => {
15
+ assert.deepEqual(
16
+ normalizeOpenCodeEvents({
17
+ type: 'tool_use',
18
+ sessionID: 'ses_main',
19
+ part: {
20
+ type: 'tool',
21
+ tool: 'task',
22
+ state: {
23
+ status: 'completed',
24
+ input: {
25
+ description: '分析 a.js 文件',
26
+ prompt: '请分析 /tmp/demo/a.js 文件',
27
+ subagent_type: 'explore',
28
+ },
29
+ output: 'task_id: ses_child_1\n\n<task_result>ok</task_result>',
30
+ metadata: {
31
+ sessionId: 'ses_child_1',
32
+ model: {
33
+ providerID: 'opencode',
34
+ modelID: 'minimax-m2.5-free',
35
+ },
36
+ },
37
+ },
38
+ },
39
+ }),
40
+ [
41
+ {
42
+ type: 'item.completed',
43
+ item: {
44
+ type: 'collab_tool_call',
45
+ tool: 'spawn_agent',
46
+ receiver_thread_ids: ['ses_child_1'],
47
+ prompt: '请分析 /tmp/demo/a.js 文件',
48
+ agents_states: {
49
+ ses_child_1: {
50
+ status: 'completed',
51
+ message: 'task_id: ses_child_1\n\n<task_result>ok</task_result>',
52
+ title: '分析 a.js 文件',
53
+ role: 'explore',
54
+ target: 'a.js',
55
+ model: 'opencode/minimax-m2.5-free',
56
+ },
57
+ },
58
+ },
59
+ },
60
+ {
61
+ type: 'item.completed',
62
+ item: {
63
+ type: 'collab_tool_call',
64
+ tool: 'wait',
65
+ receiver_thread_ids: ['ses_child_1'],
66
+ prompt: '请分析 /tmp/demo/a.js 文件',
67
+ agents_states: {
68
+ ses_child_1: {
69
+ status: 'completed',
70
+ message: 'task_id: ses_child_1\n\n<task_result>ok</task_result>',
71
+ title: '分析 a.js 文件',
72
+ role: 'explore',
73
+ target: 'a.js',
74
+ model: 'opencode/minimax-m2.5-free',
75
+ },
76
+ },
77
+ },
78
+ },
79
+ ]
80
+ )
81
+ })
82
+
83
+ test('extractOpenCodeSessionId reads common session id fields', () => {
84
+ assert.equal(extractOpenCodeSessionId({ sessionID: 'opencode-session-1' }), 'opencode-session-1')
85
+ assert.equal(extractOpenCodeSessionId({ sessionId: 'opencode-session-2' }), 'opencode-session-2')
86
+ })
87
+
88
+ test('extractOpenCodeText trims text payload', () => {
89
+ assert.equal(
90
+ extractOpenCodeText({
91
+ type: 'text',
92
+ part: {
93
+ type: 'text',
94
+ text: '\n\n已完成修改\n',
95
+ },
96
+ }),
97
+ '已完成修改'
98
+ )
99
+ })
100
+
101
+ test('normalizeOpenCodeEvent maps text output to agent message', () => {
102
+ assert.deepEqual(
103
+ normalizeOpenCodeEvent({
104
+ type: 'text',
105
+ part: {
106
+ type: 'text',
107
+ text: '已完成修改',
108
+ },
109
+ }),
110
+ {
111
+ type: 'item.completed',
112
+ item: {
113
+ type: 'agent_message',
114
+ text: '已完成修改',
115
+ },
116
+ }
117
+ )
118
+ })
119
+
120
+ test('normalizeOpenCodeEvents maps first step_start to turn.started only once', () => {
121
+ const state = createOpenCodeNormalizationState()
122
+
123
+ assert.deepEqual(
124
+ normalizeOpenCodeEvents({
125
+ type: 'step_start',
126
+ sessionID: 'ses_1',
127
+ }, state),
128
+ [{ type: 'turn.started' }]
129
+ )
130
+
131
+ assert.deepEqual(
132
+ normalizeOpenCodeEvents({
133
+ type: 'step_start',
134
+ sessionID: 'ses_1',
135
+ }, state),
136
+ []
137
+ )
138
+ })
139
+
140
+ test('normalizeOpenCodeEvents maps completed tool_use to command events', () => {
141
+ assert.deepEqual(
142
+ normalizeOpenCodeEvents({
143
+ type: 'tool_use',
144
+ sessionID: 'ses_2',
145
+ part: {
146
+ type: 'tool',
147
+ tool: 'read',
148
+ state: {
149
+ status: 'completed',
150
+ input: {
151
+ filePath: '/tmp/demo.txt',
152
+ },
153
+ output: '<path>/tmp/demo.txt</path>',
154
+ },
155
+ },
156
+ }),
157
+ [
158
+ {
159
+ type: 'item.started',
160
+ item: {
161
+ type: 'command_execution',
162
+ command: 'read: /tmp/demo.txt',
163
+ status: 'in_progress',
164
+ },
165
+ },
166
+ {
167
+ type: 'item.completed',
168
+ item: {
169
+ type: 'command_execution',
170
+ command: 'read: /tmp/demo.txt',
171
+ status: 'completed',
172
+ exit_code: 0,
173
+ aggregated_output: '<path>/tmp/demo.txt</path>',
174
+ },
175
+ },
176
+ ]
177
+ )
178
+ })
179
+
180
+ test('normalizeOpenCodeEvent maps step_finish stop to turn completion with usage', () => {
181
+ const event = normalizeOpenCodeEvent({
182
+ type: 'step_finish',
183
+ part: {
184
+ type: 'step-finish',
185
+ reason: 'stop',
186
+ tokens: {
187
+ input: 321,
188
+ output: 12,
189
+ cache: {
190
+ read: 256,
191
+ },
192
+ },
193
+ },
194
+ })
195
+
196
+ assert.deepEqual(event, {
197
+ type: 'turn.completed',
198
+ usage: {
199
+ input_tokens: 321,
200
+ output_tokens: 12,
201
+ cached_input_tokens: 256,
202
+ },
203
+ })
204
+
205
+ assert.deepEqual(extractOpenCodeUsage({
206
+ part: {
207
+ tokens: {
208
+ input: 321,
209
+ output: 12,
210
+ cache: {
211
+ read: 256,
212
+ },
213
+ },
214
+ },
215
+ }), {
216
+ input_tokens: 321,
217
+ output_tokens: 12,
218
+ cached_input_tokens: 256,
219
+ })
220
+ })
221
+
222
+ test('extractOpenCodeErrorMessage reads nested API errors', () => {
223
+ assert.equal(
224
+ extractOpenCodeErrorMessage({
225
+ type: 'error',
226
+ error: {
227
+ name: 'APIError',
228
+ data: {
229
+ message: 'openai_error',
230
+ responseBody: '{"error":{"message":"bad gateway"}}',
231
+ },
232
+ },
233
+ }),
234
+ 'openai_error'
235
+ )
236
+ })