@kkelly-offical/kkcode 0.1.7 → 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/LICENSE +674 -674
- package/README.md +452 -387
- package/package.json +50 -46
- package/src/agent/agent.mjs +228 -220
- package/src/agent/custom-agent-loader.mjs +6 -3
- package/src/agent/generator.mjs +2 -2
- package/src/agent/prompt/assistant.txt +12 -0
- package/src/agent/prompt/bug-hunter.txt +89 -89
- package/src/agent/prompt/frontend-designer.txt +58 -58
- package/src/agent/prompt/guide.txt +1 -1
- package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
- package/src/agent/prompt/longagent-coding-agent.txt +37 -37
- package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
- package/src/agent/prompt/longagent-preview-agent.txt +63 -63
- package/src/command/custom-commands.mjs +2 -2
- package/src/commands/agent.mjs +1 -1
- package/src/commands/background.mjs +145 -4
- package/src/commands/chat.mjs +117 -76
- package/src/commands/config.mjs +148 -1
- package/src/commands/doctor.mjs +30 -6
- package/src/commands/init.mjs +32 -6
- package/src/commands/longagent.mjs +117 -0
- package/src/commands/mcp.mjs +275 -43
- package/src/commands/permission.mjs +1 -1
- package/src/commands/session.mjs +195 -140
- package/src/commands/skill.mjs +63 -0
- package/src/commands/theme.mjs +1 -1
- package/src/config/defaults.mjs +280 -260
- package/src/config/import-config.mjs +1 -1
- package/src/config/load-config.mjs +61 -4
- package/src/config/schema.mjs +591 -574
- package/src/context.mjs +4 -1
- package/src/core/constants.mjs +97 -91
- package/src/core/types.mjs +1 -1
- package/src/github/api.mjs +78 -78
- package/src/github/auth.mjs +294 -286
- package/src/github/flow.mjs +298 -298
- package/src/github/workspace.mjs +225 -212
- package/src/index.mjs +84 -82
- package/src/knowledge/frontend-aesthetics.txt +38 -38
- package/src/mcp/client-http.mjs +139 -141
- package/src/mcp/client-sse.mjs +297 -288
- package/src/mcp/client-stdio.mjs +534 -533
- package/src/mcp/constants.mjs +2 -2
- package/src/mcp/registry.mjs +498 -479
- package/src/mcp/stdio-framing.mjs +135 -133
- package/src/mcp/tool-result.mjs +24 -24
- package/src/observability/edit-diagnostics.mjs +449 -0
- package/src/observability/index.mjs +42 -42
- package/src/observability/metrics.mjs +165 -137
- package/src/observability/tracer.mjs +137 -137
- package/src/onboarding.mjs +209 -0
- package/src/orchestration/background-manager.mjs +567 -372
- package/src/orchestration/background-worker.mjs +419 -305
- package/src/orchestration/interruption-reason.mjs +21 -0
- package/src/orchestration/longagent-manager.mjs +197 -171
- package/src/orchestration/stage-scheduler.mjs +733 -728
- package/src/orchestration/subagent-router.mjs +7 -1
- package/src/orchestration/task-scheduler.mjs +219 -7
- package/src/permission/engine.mjs +1 -1
- package/src/permission/exec-policy.mjs +370 -370
- package/src/permission/file-edit-policy.mjs +108 -0
- package/src/permission/prompt.mjs +1 -1
- package/src/permission/rules.mjs +116 -7
- package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
- package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
- package/src/plugin/hook-bus.mjs +19 -5
- package/src/plugin/manifest-loader.mjs +222 -0
- package/src/provider/anthropic.mjs +396 -390
- package/src/provider/ollama.mjs +7 -1
- package/src/provider/openai.mjs +382 -340
- package/src/provider/retry-policy.mjs +74 -68
- package/src/provider/router.mjs +242 -241
- package/src/provider/sse.mjs +104 -104
- package/src/provider/wizard.mjs +556 -0
- package/src/repl/capability-facade.mjs +30 -0
- package/src/repl/command-surface.mjs +23 -0
- package/src/repl/controller-entry.mjs +40 -0
- package/src/repl/core-shell.mjs +208 -0
- package/src/repl/dialog-router.mjs +87 -0
- package/src/repl/input-engine.mjs +76 -0
- package/src/repl/keymap.mjs +7 -0
- package/src/repl/operator-surface.mjs +15 -0
- package/src/repl/permission-flow.mjs +49 -0
- package/src/repl/runtime-facade.mjs +36 -0
- package/src/repl/slash-router.mjs +62 -0
- package/src/repl/state-store.mjs +29 -0
- package/src/repl/turn-controller.mjs +58 -0
- package/src/repl/verification.mjs +23 -0
- package/src/repl.mjs +3368 -2981
- package/src/rules/load-rules.mjs +3 -3
- package/src/runtime.mjs +1 -1
- package/src/session/agent-transaction.mjs +86 -0
- package/src/session/checkpoint.mjs +302 -302
- package/src/session/compaction.mjs +298 -298
- package/src/session/engine.mjs +417 -232
- package/src/session/longagent-4stage.mjs +467 -460
- package/src/session/longagent-hybrid.mjs +1344 -1097
- package/src/session/longagent-plan.mjs +376 -365
- package/src/session/longagent-project-memory.mjs +53 -53
- package/src/session/longagent-scaffold.mjs +291 -291
- package/src/session/longagent-task-bus.mjs +138 -54
- package/src/session/longagent-utils.mjs +828 -472
- package/src/session/longagent.mjs +911 -900
- package/src/session/loop.mjs +1005 -930
- package/src/session/prompt/agent.txt +25 -25
- package/src/session/prompt/anthropic.txt +150 -150
- package/src/session/prompt/beast.txt +1 -1
- package/src/session/prompt/plan.txt +31 -31
- package/src/session/prompt/qwen.txt +46 -46
- package/src/session/recovery.mjs +21 -0
- package/src/session/rollback.mjs +196 -195
- package/src/session/routing-observability.mjs +72 -0
- package/src/session/runtime-state.mjs +47 -0
- package/src/session/store.mjs +523 -519
- package/src/session/system-prompt.mjs +308 -273
- package/src/session/task-validator.mjs +267 -267
- package/src/session/usability-gates.mjs +2 -2
- package/src/skill/builtin/commit.mjs +64 -64
- package/src/skill/builtin/design.mjs +76 -76
- package/src/skill/generator.mjs +18 -2
- package/src/skill/registry.mjs +642 -390
- package/src/storage/audit-store.mjs +18 -11
- package/src/storage/event-log.mjs +7 -1
- package/src/storage/ghost-commit-store.mjs +243 -245
- package/src/storage/paths.mjs +13 -0
- package/src/theme/default-theme.mjs +1 -1
- package/src/theme/markdown.mjs +4 -0
- package/src/theme/schema.mjs +1 -1
- package/src/theme/status-bar.mjs +162 -158
- package/src/tool/audit-wrapper.mjs +18 -2
- package/src/tool/edit-transaction.mjs +23 -0
- package/src/tool/executor.mjs +26 -1
- package/src/tool/file-read-state.mjs +65 -0
- package/src/tool/git-auto.mjs +526 -526
- package/src/tool/git-full-auto.mjs +487 -478
- package/src/tool/mutation-guard.mjs +54 -0
- package/src/tool/prompt/edit.txt +3 -3
- package/src/tool/prompt/multiedit.txt +1 -0
- package/src/tool/prompt/notebookedit.txt +2 -1
- package/src/tool/prompt/patch.txt +25 -24
- package/src/tool/prompt/read.txt +3 -3
- package/src/tool/prompt/sysinfo.txt +29 -0
- package/src/tool/prompt/task.txt +66 -4
- package/src/tool/prompt/write.txt +2 -2
- package/src/tool/question-prompt.mjs +99 -93
- package/src/tool/registry.mjs +1701 -1343
- package/src/tool/task-tool.mjs +14 -6
- package/src/ui/activity-renderer.mjs +667 -664
- package/src/ui/repl-background-panel.mjs +7 -0
- package/src/ui/repl-capability-panel.mjs +9 -0
- package/src/ui/repl-dashboard.mjs +54 -4
- package/src/ui/repl-help.mjs +110 -0
- package/src/ui/repl-operator-panel.mjs +12 -0
- package/src/ui/repl-route-feedback.mjs +35 -0
- package/src/ui/repl-status-view.mjs +76 -0
- package/src/ui/repl-task-panel.mjs +5 -0
- package/src/ui/repl-transcript-panel.mjs +56 -0
- package/src/ui/repl-turn-summary.mjs +135 -0
- package/src/usage/pricing.mjs +122 -121
- package/src/usage/usage-meter.mjs +1 -0
- package/src/util/git.mjs +562 -519
- package/src/util/template.mjs +6 -1
|
@@ -1,472 +1,828 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
text.includes("
|
|
57
|
-
text.includes("
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
text.includes("
|
|
70
|
-
text.includes("
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
let
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
return
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
1
|
+
import { detectAgentContinuationInput, extractPromptPathHints } from "./agent-transaction.mjs"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LongAgent 共享工具函数
|
|
5
|
+
* 被 longagent.mjs、longagent-hybrid.mjs 共同使用
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const LONGAGENT_FILE_CHANGES_LIMIT = 400
|
|
9
|
+
|
|
10
|
+
// ========== 共享 JSON 解析工具 ==========
|
|
11
|
+
|
|
12
|
+
export function stripFence(text = "") {
|
|
13
|
+
const raw = String(text || "").trim()
|
|
14
|
+
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i)
|
|
15
|
+
return fenced ? fenced[1].trim() : raw
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function parseJsonLoose(text = "") {
|
|
19
|
+
const raw = stripFence(text)
|
|
20
|
+
// 1. 直接解析
|
|
21
|
+
try { return JSON.parse(raw) } catch { /* ignore */ }
|
|
22
|
+
// 2. 修复 trailing comma(LLM 常产出 {…, } 或 […, ])
|
|
23
|
+
const repaired = raw.replace(/,\s*([}\]])/g, "$1")
|
|
24
|
+
if (repaired !== raw) {
|
|
25
|
+
try { return JSON.parse(repaired) } catch { /* ignore */ }
|
|
26
|
+
}
|
|
27
|
+
// 3. 提取最外层 {} 块
|
|
28
|
+
const start = raw.indexOf("{")
|
|
29
|
+
const end = raw.lastIndexOf("}")
|
|
30
|
+
if (start >= 0 && end > start) {
|
|
31
|
+
const slice = raw.slice(start, end + 1)
|
|
32
|
+
try { return JSON.parse(slice) } catch { /* ignore */ }
|
|
33
|
+
// 3b. 对提取的块也尝试 trailing comma 修复
|
|
34
|
+
const sliceRepaired = slice.replace(/,\s*([}\]])/g, "$1")
|
|
35
|
+
if (sliceRepaired !== slice) {
|
|
36
|
+
try { return JSON.parse(sliceRepaired) } catch { /* ignore */ }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ========== Phase 1: 错误分类 ==========
|
|
43
|
+
|
|
44
|
+
export const ERROR_CATEGORIES = {
|
|
45
|
+
TRANSIENT: "transient",
|
|
46
|
+
LOGIC: "logic",
|
|
47
|
+
PERMANENT: "permanent",
|
|
48
|
+
UNKNOWN: "unknown"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function classifyError(errorText, bgStatus) {
|
|
52
|
+
const text = String(errorText || "").toLowerCase()
|
|
53
|
+
|
|
54
|
+
// transient: 网络/超时/限流/worker 消失
|
|
55
|
+
if (
|
|
56
|
+
text.includes("timeout") || text.includes("timed out") ||
|
|
57
|
+
text.includes("econnreset") || text.includes("econnrefused") ||
|
|
58
|
+
text.includes("enotfound") || text.includes("socket hang up") ||
|
|
59
|
+
text.includes("rate limit") || text.includes("429") ||
|
|
60
|
+
text.includes("503") || text.includes("502") ||
|
|
61
|
+
text.includes("worker disappeared") || text.includes("background worker disappeared") ||
|
|
62
|
+
bgStatus === "interrupted"
|
|
63
|
+
) {
|
|
64
|
+
return ERROR_CATEGORIES.TRANSIENT
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// permanent: 文件不存在/权限不足/配置缺失
|
|
68
|
+
if (
|
|
69
|
+
text.includes("enoent") || text.includes("no such file") ||
|
|
70
|
+
text.includes("eacces") || text.includes("eperm") || text.includes("permission denied") ||
|
|
71
|
+
text.includes("config missing") || text.includes("configuration not found") ||
|
|
72
|
+
text.includes("module not found") || text.includes("cannot find module") ||
|
|
73
|
+
bgStatus === "cancelled"
|
|
74
|
+
) {
|
|
75
|
+
return ERROR_CATEGORIES.PERMANENT
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// logic: 代码 bug/类型错误/语法错误
|
|
79
|
+
if (
|
|
80
|
+
text.includes("syntaxerror") || text.includes("syntax error") ||
|
|
81
|
+
text.includes("typeerror") || text.includes("type error") ||
|
|
82
|
+
text.includes("referenceerror") || text.includes("reference error") ||
|
|
83
|
+
text.includes("rangeerror") || text.includes("assertionerror") ||
|
|
84
|
+
text.includes("unexpected token") || text.includes("is not a function") ||
|
|
85
|
+
text.includes("is not defined") || text.includes("cannot read propert")
|
|
86
|
+
) {
|
|
87
|
+
return ERROR_CATEGORIES.LOGIC
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 默认: 未知类别,不自动重试(避免在不可恢复错误上浪费资源)
|
|
91
|
+
return ERROR_CATEGORIES.UNKNOWN
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function isComplete(text) {
|
|
95
|
+
const lower = String(text || "").toLowerCase()
|
|
96
|
+
if (lower.includes("[task_complete]")) return true
|
|
97
|
+
// Only match "task complete" as a standalone phrase, not substring of other text
|
|
98
|
+
if (/\btask[\s_-]?complete\b/.test(lower)) return true
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function isLikelyActionableObjective(prompt) {
|
|
103
|
+
const text = String(prompt || "").trim()
|
|
104
|
+
if (!text) return false
|
|
105
|
+
const lower = text.toLowerCase()
|
|
106
|
+
const greetings = [
|
|
107
|
+
"hi", "hello", "hey", "你好", "您好", "在吗", "yo", "嗨"
|
|
108
|
+
]
|
|
109
|
+
const codingSignals = [
|
|
110
|
+
"fix", "build", "implement", "refactor", "debug", "test", "review", "write", "create", "add", "optimize", "migrate", "deploy",
|
|
111
|
+
"bug", "issue", "error", "code", "repo", "file", "function", "api",
|
|
112
|
+
"修复", "实现", "重构", "调试", "测试", "优化", "迁移", "部署", "代码", "仓库", "文件", "函数", "接口", "需求", "功能", "报错"
|
|
113
|
+
]
|
|
114
|
+
if (codingSignals.some((kw) => lower.includes(kw))) return true
|
|
115
|
+
if (greetings.some((g) => lower === g || lower === `${g}!` || lower === `${g}!`)) return false
|
|
116
|
+
if (text.length <= 8 && !/[./\\:_-]/.test(text)) return false
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function summarizeGateFailures(failures = []) {
|
|
121
|
+
if (!failures.length) return ""
|
|
122
|
+
return failures
|
|
123
|
+
.slice(0, 5)
|
|
124
|
+
.map((item) => `${item.gate}:${item.reason}`)
|
|
125
|
+
.join("; ")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function stageProgressStats(taskProgress = {}) {
|
|
129
|
+
if (!taskProgress || typeof taskProgress !== "object") {
|
|
130
|
+
return { done: 0, total: 0, remainingFiles: [], remainingFilesCount: 0 }
|
|
131
|
+
}
|
|
132
|
+
const items = Object.values(taskProgress)
|
|
133
|
+
const done = items.filter((item) => item.status === "completed").length
|
|
134
|
+
const total = items.length
|
|
135
|
+
const remainingFiles = [...new Set(items.flatMap((item) => Array.isArray(item.remainingFiles) ? item.remainingFiles : []))]
|
|
136
|
+
return { done, total, remainingFiles, remainingFilesCount: remainingFiles.length }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function normalizeFileChange(item = {}) {
|
|
140
|
+
const path = String(item.path || "").trim()
|
|
141
|
+
if (!path) return null
|
|
142
|
+
return {
|
|
143
|
+
path,
|
|
144
|
+
addedLines: Math.max(0, Number(item.addedLines || 0)),
|
|
145
|
+
removedLines: Math.max(0, Number(item.removedLines || 0)),
|
|
146
|
+
stageId: item.stageId ? String(item.stageId) : "",
|
|
147
|
+
taskId: item.taskId ? String(item.taskId) : ""
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ========== 防卡死机制 (ported from Mark's anti-stuck work) ==========
|
|
152
|
+
|
|
153
|
+
export const READ_ONLY_TOOLS = new Set(["read", "glob", "grep", "list", "webfetch", "websearch", "codesearch"])
|
|
154
|
+
|
|
155
|
+
export function isReadOnlyTool(name) {
|
|
156
|
+
return READ_ONLY_TOOLS.has(name)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 检测配置文件搜索循环(Qwen 等模型容易反复 glob 配置文件)
|
|
161
|
+
*/
|
|
162
|
+
export function detectExplorationLoop(recentToolCalls) {
|
|
163
|
+
const recentGlobs = recentToolCalls.slice(-10).filter(sig => sig.startsWith("glob:"))
|
|
164
|
+
if (recentGlobs.length >= 6) {
|
|
165
|
+
const patterns = recentGlobs.map(sig => {
|
|
166
|
+
try { return JSON.parse(sig.slice(5)).pattern } catch { return null }
|
|
167
|
+
}).filter(Boolean)
|
|
168
|
+
const configPatterns = [/pyproject\.toml/, /setup\.py/, /Pipfile/, /Dockerfile/, /\.env/, /main\.py/, /package\.json/, /tsconfig\.json/]
|
|
169
|
+
const matched = patterns.filter(p => configPatterns.some(cp => cp.test(p)))
|
|
170
|
+
if (matched.length >= 4) return { isLoop: true, reason: "repeated_config_file_glob" }
|
|
171
|
+
}
|
|
172
|
+
return { isLoop: false }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 检测工具调用循环(同类工具重复 / 前后半段完全相同)
|
|
177
|
+
*/
|
|
178
|
+
export function detectToolCycle(recentToolCalls) {
|
|
179
|
+
if (recentToolCalls.length < 6) return false
|
|
180
|
+
// 同类工具连续 6 次
|
|
181
|
+
const recentTypes = recentToolCalls.slice(-6).map(sig => sig.split(":")[0])
|
|
182
|
+
if (recentTypes.every(t => t === recentTypes[0]) && isReadOnlyTool(recentTypes[0])) return true
|
|
183
|
+
// 前后半段按序列比较(保留顺序信息,避免排序后误报)
|
|
184
|
+
const allReadOnly = recentToolCalls.every(sig => isReadOnlyTool(sig.split(":")[0]))
|
|
185
|
+
if (allReadOnly) {
|
|
186
|
+
const half = Math.floor(recentToolCalls.length / 2)
|
|
187
|
+
if (half >= 3) {
|
|
188
|
+
const first = recentToolCalls.slice(0, half).join(",")
|
|
189
|
+
const second = recentToolCalls.slice(half, half * 2).join(",")
|
|
190
|
+
if (first === second) return true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 创建一个有状态的卡死追踪器,供各模式的主循环使用
|
|
198
|
+
*/
|
|
199
|
+
// ========== Phase 4: 写操作循环检测 ==========
|
|
200
|
+
|
|
201
|
+
const WRITE_TOOLS = new Set(["write", "edit", "notebookedit"])
|
|
202
|
+
|
|
203
|
+
function isWriteTool(name) {
|
|
204
|
+
return WRITE_TOOLS.has(name)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function detectWriteLoop(recentWriteOps) {
|
|
208
|
+
if (recentWriteOps.length < 3) return { isLoop: false, reason: null }
|
|
209
|
+
|
|
210
|
+
// 检测同一文件被连续 edit 3+ 次
|
|
211
|
+
const last3 = recentWriteOps.slice(-3)
|
|
212
|
+
if (last3.every(op => op.path === last3[0].path && op.tool === "edit")) {
|
|
213
|
+
return { isLoop: true, reason: "write_loop_detected" }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 检测 write→error→edit→error 循环(同一文件交替出现)
|
|
217
|
+
if (recentWriteOps.length >= 4) {
|
|
218
|
+
const last4 = recentWriteOps.slice(-4)
|
|
219
|
+
const samePath = last4.every(op => op.path === last4[0].path)
|
|
220
|
+
if (samePath) {
|
|
221
|
+
const tools = last4.map(op => op.tool)
|
|
222
|
+
const hasAlternation = (tools[0] === "write" && tools[2] === "edit") ||
|
|
223
|
+
(tools[0] === "edit" && tools[2] === "write")
|
|
224
|
+
if (hasAlternation) return { isLoop: true, reason: "edit_cycle_detected" }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { isLoop: false, reason: null }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function createStuckTracker(maxRecent = 10) {
|
|
232
|
+
const recentToolCalls = []
|
|
233
|
+
const recentWriteOps = []
|
|
234
|
+
let consecutiveReadOnlyCount = 0
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
/** 记录本轮 tool events,返回 { isStuck, reason } */
|
|
238
|
+
track(toolEvents = []) {
|
|
239
|
+
const sigs = toolEvents.map(e => `${e.name}:${JSON.stringify(e.args || {})}`)
|
|
240
|
+
recentToolCalls.push(...sigs)
|
|
241
|
+
while (recentToolCalls.length > maxRecent) recentToolCalls.shift()
|
|
242
|
+
|
|
243
|
+
// Phase 4: 追踪写操作
|
|
244
|
+
for (const e of toolEvents) {
|
|
245
|
+
if (isWriteTool(e.name)) {
|
|
246
|
+
recentWriteOps.push({
|
|
247
|
+
tool: e.name,
|
|
248
|
+
path: String(e.args?.path || e.args?.file_path || "").trim(),
|
|
249
|
+
lineRange: e.args?.old_string ? e.args.old_string.slice(0, 50) : ""
|
|
250
|
+
})
|
|
251
|
+
while (recentWriteOps.length > maxRecent) recentWriteOps.shift()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const allReadOnly = toolEvents.length > 0 && toolEvents.every(e => isReadOnlyTool(e.name))
|
|
256
|
+
if (allReadOnly) consecutiveReadOnlyCount++
|
|
257
|
+
else consecutiveReadOnlyCount = 0
|
|
258
|
+
|
|
259
|
+
const loop = detectExplorationLoop(recentToolCalls)
|
|
260
|
+
if (loop.isLoop) return { isStuck: true, reason: loop.reason }
|
|
261
|
+
if (detectToolCycle(recentToolCalls)) return { isStuck: true, reason: "tool_cycle_detected" }
|
|
262
|
+
if (consecutiveReadOnlyCount >= 4) return { isStuck: true, reason: "excessive_read_only_exploration" }
|
|
263
|
+
|
|
264
|
+
// Phase 4: 写循环检测
|
|
265
|
+
const writeLoop = detectWriteLoop(recentWriteOps)
|
|
266
|
+
if (writeLoop.isLoop) return { isStuck: true, reason: writeLoop.reason }
|
|
267
|
+
|
|
268
|
+
return { isStuck: false, reason: null }
|
|
269
|
+
},
|
|
270
|
+
/** 重置连续只读计数(警告注入后调用) */
|
|
271
|
+
resetReadOnlyCount() { consecutiveReadOnlyCount = 0 },
|
|
272
|
+
get consecutiveReadOnly() { return consecutiveReadOnlyCount },
|
|
273
|
+
get writeOps() { return recentWriteOps }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function mergeCappedFileChanges(current = [], incoming = [], limit = LONGAGENT_FILE_CHANGES_LIMIT) {
|
|
278
|
+
const maxEntries = Math.max(1, Number(limit || LONGAGENT_FILE_CHANGES_LIMIT))
|
|
279
|
+
const map = new Map()
|
|
280
|
+
|
|
281
|
+
const append = (entry) => {
|
|
282
|
+
const normalized = normalizeFileChange(entry)
|
|
283
|
+
if (!normalized) return
|
|
284
|
+
const key = `${normalized.path}::${normalized.stageId}::${normalized.taskId}`
|
|
285
|
+
const prev = map.get(key) || { ...normalized, addedLines: 0, removedLines: 0 }
|
|
286
|
+
prev.addedLines += normalized.addedLines
|
|
287
|
+
prev.removedLines += normalized.removedLines
|
|
288
|
+
map.delete(key)
|
|
289
|
+
map.set(key, prev)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (const item of current) append(item)
|
|
293
|
+
for (const item of incoming) append(item)
|
|
294
|
+
|
|
295
|
+
const merged = [...map.values()]
|
|
296
|
+
if (merged.length > maxEntries) {
|
|
297
|
+
const truncated = merged.slice(merged.length - maxEntries)
|
|
298
|
+
truncated._truncatedFrom = merged.length
|
|
299
|
+
return truncated
|
|
300
|
+
}
|
|
301
|
+
return merged
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ========== Phase 5: 语义级错误检测 ==========
|
|
305
|
+
|
|
306
|
+
export function createSemanticErrorTracker(threshold = 3) {
|
|
307
|
+
const errorHistory = []
|
|
308
|
+
|
|
309
|
+
function extractErrorPattern(text) {
|
|
310
|
+
const str = String(text || "")
|
|
311
|
+
const patterns = []
|
|
312
|
+
const errorRegex = /(?:TypeError|ReferenceError|SyntaxError|RangeError|Error|AssertionError):\s*(.+?)(?:\n|$)/gi
|
|
313
|
+
let m
|
|
314
|
+
while ((m = errorRegex.exec(str)) !== null) {
|
|
315
|
+
patterns.push(m[0].trim().slice(0, 120))
|
|
316
|
+
}
|
|
317
|
+
return patterns
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function isSimilar(a, b) {
|
|
321
|
+
if (a === b) return true
|
|
322
|
+
if (a.length < 10 || b.length < 10) return a === b
|
|
323
|
+
// Token-level Jaccard similarity — more robust than substring matching
|
|
324
|
+
const tokenize = (s) => new Set(s.toLowerCase().split(/[\s:.'"`()\[\]{}]+/).filter(t => t.length > 2))
|
|
325
|
+
const setA = tokenize(a)
|
|
326
|
+
const setB = tokenize(b)
|
|
327
|
+
if (!setA.size || !setB.size) return false
|
|
328
|
+
let intersection = 0
|
|
329
|
+
for (const t of setA) { if (setB.has(t)) intersection++ }
|
|
330
|
+
const union = setA.size + setB.size - intersection
|
|
331
|
+
return union > 0 && (intersection / union) >= 0.6
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
track(replyText) {
|
|
336
|
+
const patterns = extractErrorPattern(replyText)
|
|
337
|
+
if (!patterns.length) {
|
|
338
|
+
errorHistory.push(null)
|
|
339
|
+
return { isRepeated: false, error: null, count: 0 }
|
|
340
|
+
}
|
|
341
|
+
const primary = patterns[0]
|
|
342
|
+
errorHistory.push(primary)
|
|
343
|
+
|
|
344
|
+
// 检查最近 threshold 次是否出现相同错误
|
|
345
|
+
if (errorHistory.length >= threshold) {
|
|
346
|
+
const recent = errorHistory.slice(-threshold).filter(Boolean)
|
|
347
|
+
if (recent.length === threshold && recent.every(e => isSimilar(e, primary))) {
|
|
348
|
+
return { isRepeated: true, error: primary, count: threshold }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return { isRepeated: false, error: primary, count: 1 }
|
|
352
|
+
},
|
|
353
|
+
reset() { errorHistory.length = 0 },
|
|
354
|
+
get history() { return errorHistory }
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ========== Phase 6: 渐进式降级策略 ==========
|
|
359
|
+
|
|
360
|
+
export function createDegradationChain(config = {}) {
|
|
361
|
+
const strategies = [
|
|
362
|
+
{
|
|
363
|
+
name: "switch_model",
|
|
364
|
+
apply(ctx) {
|
|
365
|
+
const fallback = config.fallback_model
|
|
366
|
+
if (!fallback || ctx.model === fallback) return false
|
|
367
|
+
ctx.previousModel = ctx.model
|
|
368
|
+
ctx.model = fallback
|
|
369
|
+
return true
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
name: "reduce_scope",
|
|
374
|
+
apply(ctx) {
|
|
375
|
+
if (!config.skip_non_critical || !ctx.taskProgress) return false
|
|
376
|
+
let skipped = 0
|
|
377
|
+
for (const [taskId, tp] of Object.entries(ctx.taskProgress)) {
|
|
378
|
+
if (tp.status === "error" || tp.status === "retrying") {
|
|
379
|
+
ctx.taskProgress[taskId] = { ...tp, status: "skipped", skipReason: "degradation_reduce_scope" }
|
|
380
|
+
skipped++
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return skipped > 0
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "serial_mode",
|
|
388
|
+
apply(ctx) {
|
|
389
|
+
if (!ctx.configState?.config?.agent?.longagent?.parallel) return false
|
|
390
|
+
ctx.configState.config.agent.longagent.parallel.max_concurrency = 1
|
|
391
|
+
return true
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "graceful_stop",
|
|
396
|
+
apply(ctx) {
|
|
397
|
+
ctx.shouldStop = true
|
|
398
|
+
return true
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
let currentLevel = 0
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
canDegrade() { return currentLevel < strategies.length },
|
|
407
|
+
currentStrategy() { return strategies[currentLevel] || null },
|
|
408
|
+
nextStrategy() { return strategies[currentLevel] || null },
|
|
409
|
+
apply(ctx) {
|
|
410
|
+
if (currentLevel >= strategies.length) return { applied: false, strategy: null }
|
|
411
|
+
const strategy = strategies[currentLevel]
|
|
412
|
+
const applied = strategy.apply(ctx)
|
|
413
|
+
if (applied) currentLevel++
|
|
414
|
+
return { applied, strategy: strategy.name }
|
|
415
|
+
},
|
|
416
|
+
get level() { return currentLevel }
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ========== Task 1: 智能任务模式分类 ==========
|
|
421
|
+
|
|
422
|
+
const MODE_REASON_EXPLANATIONS = {
|
|
423
|
+
empty_input: "空输入,按问答处理",
|
|
424
|
+
question_with_explain_intent: "检测到问答 / 解释意图",
|
|
425
|
+
short_question: "检测到简短问答",
|
|
426
|
+
planning_or_design_intent: "检测到规划 / 设计意图",
|
|
427
|
+
long_complex_prompt: "检测到长而复杂的任务描述,可能需要 longagent",
|
|
428
|
+
short_local_task_protected: "检测到短小本地事务,避免升级到 longagent",
|
|
429
|
+
local_transaction_task: "检测到本地事务型任务,适合保持在轻量 agent 路径",
|
|
430
|
+
assistant_local_task: "检测到终端个人助手任务",
|
|
431
|
+
local_lookup_task: "检测到本地读取 / 总结类任务",
|
|
432
|
+
single_path_or_command_task: "检测到单路径或单命令任务,适合保持在轻量路径",
|
|
433
|
+
multi_file_or_system_task: "检测到跨文件 / 系统级任务",
|
|
434
|
+
broad_scope_multi_step: "检测到宽范围多步骤任务",
|
|
435
|
+
simple_action_task: "检测到单轮执行任务",
|
|
436
|
+
default_agent: "信号偏执行型,保持 agent",
|
|
437
|
+
default_assistant: "信号不足,按 assistant 处理",
|
|
438
|
+
low_confidence: "信号不足,保持当前模式",
|
|
439
|
+
plan_mode_exempt: "plan 模式不参与自动路由"
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function explainTaskModeReason(reason) {
|
|
443
|
+
return MODE_REASON_EXPLANATIONS[reason] || String(reason || "unknown")
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function countPromptMatches(patterns, input) {
|
|
447
|
+
return patterns.reduce((count, pattern) => count + (pattern.test(input) ? 1 : 0), 0)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 分析 prompt,判断最适合的执行模式
|
|
452
|
+
* @returns {{ mode: 'assistant'|'plan'|'agent'|'longagent', confidence: 'high'|'medium'|'low', reason: string }}
|
|
453
|
+
*/
|
|
454
|
+
export function classifyTaskMode(prompt, options = {}) {
|
|
455
|
+
const text = String(prompt || "").trim()
|
|
456
|
+
if (!text) return { mode: "assistant", confidence: "high", reason: "empty_input" }
|
|
457
|
+
|
|
458
|
+
const continuation = options?.continuation || null
|
|
459
|
+
const lower = text.toLowerCase()
|
|
460
|
+
const len = text.length
|
|
461
|
+
const pathHints = extractPromptPathHints(text)
|
|
462
|
+
const hasPathHint = pathHints.length > 0
|
|
463
|
+
|
|
464
|
+
const questionPatterns = [
|
|
465
|
+
/^(what|how|why|when|where|who|which|explain|tell me|describe|show me)\b/i,
|
|
466
|
+
/^(什么|为什么|怎么|如何|哪里|哪个|谁|能否|请解释|告诉我|描述|是什么|有什么|怎样)/,
|
|
467
|
+
/[??]\s*$/
|
|
468
|
+
]
|
|
469
|
+
const pureAssistantKeywords = [
|
|
470
|
+
"explain", "what is", "what are", "how does", "why does", "describe", "tell me about",
|
|
471
|
+
"解释", "是什么", "为什么", "怎么理解", "什么意思", "有什么区别", "如何理解"
|
|
472
|
+
]
|
|
473
|
+
const planPatterns = [
|
|
474
|
+
/\b(plan|design|architect|outline|blueprint|draft|propose|sketch)\b/i,
|
|
475
|
+
/\b(规划|设计|架构|方案|蓝图|草案|提案|计划一下|帮我想想)\b/i
|
|
476
|
+
]
|
|
477
|
+
const explicitHeavyScopePatterns = [
|
|
478
|
+
/\b(multiple files?|across files?|entire (codebase|project|repo)|all files?|cross[- ]repo|跨文件|多个文件|整个项目|全量)\b/i,
|
|
479
|
+
/\b(refactor|rewrite|overhaul|redesign|migrate).{0,30}(system|service|architecture|repo|project|module|pipeline|codebase)\b/i
|
|
480
|
+
]
|
|
481
|
+
const heavyDeliveryPatterns = [
|
|
482
|
+
/\b(implement|build|create|develop|add).{0,40}(system|subsystem|service|feature|component|framework|pipeline|architecture|架构|系统|模块|服务|功能|组件|框架|流水线)\b/i,
|
|
483
|
+
/\b(full|complete|comprehensive|end.to.end|完整实现|完全|端到端)\b/i,
|
|
484
|
+
/\b(multi.?stage|multi.?step|phases?|多阶段|多步骤|分阶段)\b/i
|
|
485
|
+
]
|
|
486
|
+
const inspectPatterns = [
|
|
487
|
+
/\b(run|execute|check|inspect|look at|read|open|summari[sz]e|scan|search|find|list|grep|tail|cat|count|compare|verify|show)\b/i,
|
|
488
|
+
/\b(日志|目录|文件|配置|仓库|看一下|检查|查看|读取|总结|搜一下|列出|执行|运行|验证)\b/i
|
|
489
|
+
]
|
|
490
|
+
const patchPatterns = [
|
|
491
|
+
/\b(fix|debug|patch|update|change|modify|rename|delete|remove|add|insert|append)\b/i,
|
|
492
|
+
/\b(修复|调试|修改|更新|删除|添加|插入|改一下|帮我改|帮我加)\b/i
|
|
493
|
+
]
|
|
494
|
+
const singleCommandPatterns = [
|
|
495
|
+
/`[^`]+`/,
|
|
496
|
+
/\b(npm|pnpm|yarn|node|git|ls|cat|grep|rg|find|sed|awk|tail|head)\b/i
|
|
497
|
+
]
|
|
498
|
+
const verifyPatterns = [
|
|
499
|
+
/\b(test|verify|validate|confirm|make sure|ensure|smoke)\b/i,
|
|
500
|
+
/\b(验证|确认|测试|确保|冒烟)\b/i
|
|
501
|
+
]
|
|
502
|
+
|
|
503
|
+
const isQuestion = questionPatterns.some((re) => re.test(text))
|
|
504
|
+
const isPureAssistantRequest = pureAssistantKeywords.some((kw) => lower.includes(kw))
|
|
505
|
+
const isPlan = planPatterns.some((re) => re.test(lower))
|
|
506
|
+
const explicitHeavyScope = explicitHeavyScopePatterns.some((re) => re.test(lower))
|
|
507
|
+
const heavyDelivery = heavyDeliveryPatterns.some((re) => re.test(lower))
|
|
508
|
+
const hasAcrossScope = /\bacross\b|跨/.test(lower)
|
|
509
|
+
const isLocalTask = inspectPatterns.some((re) => re.test(lower))
|
|
510
|
+
const isPatchTask = patchPatterns.some((re) => re.test(lower))
|
|
511
|
+
const isVerifyTask = verifyPatterns.some((re) => re.test(lower))
|
|
512
|
+
const isAgentAction = isPatchTask || isVerifyTask
|
|
513
|
+
const isSingleCommandTask = singleCommandPatterns.some((re) => re.test(text))
|
|
514
|
+
const isVerificationTask = isVerifyTask
|
|
515
|
+
const hasContinuationSignal = Boolean(options?.continued || continuation?.objective || detectAgentContinuationInput(text, continuation))
|
|
516
|
+
const isLongAgent = explicitHeavyScope || heavyDelivery
|
|
517
|
+
const localSignalCount = [isLocalTask, isPatchTask, isVerifyTask, hasPathHint, isSingleCommandTask, hasContinuationSignal].filter(Boolean).length
|
|
518
|
+
const heavySignalCount = [explicitHeavyScope, heavyDelivery, hasAcrossScope].filter(Boolean).length
|
|
519
|
+
const smallBoundedPathSet = hasPathHint && pathHints.length <= 3
|
|
520
|
+
const isBoundedLocalTask = !isLongAgent && (isLocalTask || isAgentAction) && (hasPathHint || isSingleCommandTask || len < 320)
|
|
521
|
+
const isInspectPatchVerifyLoop = isLocalTask && isAgentAction && isVerificationTask
|
|
522
|
+
const evidence = []
|
|
523
|
+
if (hasPathHint) evidence.push(hasPathHint && pathHints.length === 1 ? "single_path" : "bounded_file_set")
|
|
524
|
+
if (isSingleCommandTask) evidence.push("single_command")
|
|
525
|
+
if (isLocalTask) evidence.push("inspect")
|
|
526
|
+
if (isPatchTask) evidence.push("patch")
|
|
527
|
+
if (isVerifyTask) evidence.push("verify")
|
|
528
|
+
if (isInspectPatchVerifyLoop) evidence.push("inspect_patch_verify")
|
|
529
|
+
if (isPlan && localSignalCount >= 2 && !explicitHeavyScope) evidence.push("embedded_planning_language")
|
|
530
|
+
if (hasContinuationSignal) evidence.push("continuation_context")
|
|
531
|
+
if (explicitHeavyScope || hasAcrossScope) evidence.push("cross_file_scope")
|
|
532
|
+
if (heavyDelivery) evidence.push("heavy_delivery")
|
|
533
|
+
|
|
534
|
+
let topology = "open_ended"
|
|
535
|
+
if (explicitHeavyScope || (heavyDelivery && heavySignalCount >= 2 && localSignalCount <= 2)) {
|
|
536
|
+
topology = "heavy_multi_file_delivery"
|
|
537
|
+
} else if (isInspectPatchVerifyLoop || (localSignalCount >= 3 && smallBoundedPathSet)) {
|
|
538
|
+
topology = "bounded_local_transaction"
|
|
539
|
+
} else if (hasContinuationSignal) {
|
|
540
|
+
topology = "continued_local_transaction"
|
|
541
|
+
} else if (isLocalTask || hasPathHint || isSingleCommandTask) {
|
|
542
|
+
topology = "bounded_lookup"
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const scores = { assistant: 0, plan: 0, agent: 0, longagent: 0 }
|
|
546
|
+
const reasons = {
|
|
547
|
+
assistant: "assistant_local_task",
|
|
548
|
+
plan: "planning_or_design_intent",
|
|
549
|
+
agent: "default_agent",
|
|
550
|
+
longagent: "default_longagent"
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (isQuestion) {
|
|
554
|
+
evidence.push("question_intent")
|
|
555
|
+
scores.assistant += len < 120 ? 4 : 3
|
|
556
|
+
reasons.assistant = len < 80 ? "short_question" : "question_with_explain_intent"
|
|
557
|
+
}
|
|
558
|
+
if (isPureAssistantRequest) {
|
|
559
|
+
evidence.push("pure_explanation_request")
|
|
560
|
+
scores.assistant += 3
|
|
561
|
+
reasons.assistant = "question_with_explain_intent"
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (isPlan) {
|
|
565
|
+
evidence.push("planning_language")
|
|
566
|
+
scores.plan += 4
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (isLongAgent) {
|
|
570
|
+
evidence.push("heavy_scope_signal")
|
|
571
|
+
scores.longagent += 6
|
|
572
|
+
reasons.longagent = "multi_file_or_system_task"
|
|
573
|
+
}
|
|
574
|
+
if (heavyDelivery) {
|
|
575
|
+
scores.longagent += explicitHeavyScope ? 2 : 3
|
|
576
|
+
if (reasons.longagent === "default_longagent") reasons.longagent = "multi_file_or_system_task"
|
|
577
|
+
}
|
|
578
|
+
if (hasAcrossScope && heavyDelivery) {
|
|
579
|
+
scores.longagent += 3
|
|
580
|
+
reasons.longagent = "multi_file_or_system_task"
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (isLocalTask) {
|
|
584
|
+
evidence.push("local_task_signal")
|
|
585
|
+
scores.assistant += 4
|
|
586
|
+
reasons.assistant = "local_lookup_task"
|
|
587
|
+
}
|
|
588
|
+
if (isAgentAction) {
|
|
589
|
+
evidence.push("mutation_signal")
|
|
590
|
+
scores.agent += 3
|
|
591
|
+
if (reasons.agent === "default_agent") reasons.agent = "simple_action_task"
|
|
592
|
+
}
|
|
593
|
+
if (isVerifyTask) {
|
|
594
|
+
scores.agent += 2
|
|
595
|
+
if (reasons.agent === "default_agent") reasons.agent = "simple_action_task"
|
|
596
|
+
}
|
|
597
|
+
if (isInspectPatchVerifyLoop) {
|
|
598
|
+
scores.agent += 2
|
|
599
|
+
reasons.agent = "local_transaction_task"
|
|
600
|
+
}
|
|
601
|
+
if (hasContinuationSignal) {
|
|
602
|
+
scores.agent += 3
|
|
603
|
+
reasons.agent = "local_transaction_task"
|
|
604
|
+
}
|
|
605
|
+
if (isAgentAction && localSignalCount >= 2 && !explicitHeavyScope) {
|
|
606
|
+
scores.agent += 3
|
|
607
|
+
reasons.agent = "local_transaction_task"
|
|
608
|
+
}
|
|
609
|
+
if (hasPathHint || isSingleCommandTask) {
|
|
610
|
+
evidence.push(hasPathHint ? "path_hint" : "single_command")
|
|
611
|
+
scores.assistant += 2
|
|
612
|
+
if (reasons.assistant === "assistant_local_task") reasons.assistant = "single_path_or_command_task"
|
|
613
|
+
}
|
|
614
|
+
if (isVerificationTask) {
|
|
615
|
+
evidence.push("verification_signal")
|
|
616
|
+
}
|
|
617
|
+
if (isBoundedLocalTask) {
|
|
618
|
+
evidence.push("bounded_local_scope")
|
|
619
|
+
scores.assistant += 2
|
|
620
|
+
if (isAgentAction) scores.agent += 2
|
|
621
|
+
}
|
|
622
|
+
if (isInspectPatchVerifyLoop) {
|
|
623
|
+
evidence.push("inspect_patch_verify_loop")
|
|
624
|
+
scores.agent += 2
|
|
625
|
+
scores.longagent = Math.max(0, scores.longagent - 4)
|
|
626
|
+
reasons.agent = "short_local_task_protected"
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (len > 500 && !isQuestion && !isLocalTask && !isAgentAction && !hasPathHint && !isSingleCommandTask) {
|
|
630
|
+
evidence.push("long_prompt")
|
|
631
|
+
scores.longagent += 2
|
|
632
|
+
if (reasons.longagent === "default_longagent") reasons.longagent = "long_complex_prompt"
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if ((len < 240 || isBoundedLocalTask) && (isLocalTask || isAgentAction || hasPathHint || isSingleCommandTask) && !isLongAgent) {
|
|
636
|
+
scores.longagent = Math.max(0, scores.longagent - 3)
|
|
637
|
+
if (isAgentAction && (reasons.agent === "local_transaction_task" || reasons.agent === "single_path_or_command_task")) {
|
|
638
|
+
reasons.agent = "short_local_task_protected"
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (!isQuestion && len > 50) {
|
|
643
|
+
scores.assistant += 1
|
|
644
|
+
if (isAgentAction) scores.agent += 1
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (scores.plan >= 4 && scores.plan >= scores.longagent + 2 && scores.plan >= scores.agent + 1 && len < 240) {
|
|
648
|
+
return {
|
|
649
|
+
mode: "plan",
|
|
650
|
+
confidence: scores.plan >= 5 ? "high" : "medium",
|
|
651
|
+
reason: reasons.plan,
|
|
652
|
+
evidence,
|
|
653
|
+
topology,
|
|
654
|
+
pathHints,
|
|
655
|
+
continuity: hasContinuationSignal ? "continue_current_transaction" : "new_transaction"
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (scores.longagent >= Math.max(scores.assistant, scores.agent) + 2 && scores.longagent > 0) {
|
|
660
|
+
return {
|
|
661
|
+
mode: "longagent",
|
|
662
|
+
confidence: scores.longagent >= 6 ? "high" : "medium",
|
|
663
|
+
reason: reasons.longagent === "default_longagent" ? "long_complex_prompt" : reasons.longagent,
|
|
664
|
+
evidence,
|
|
665
|
+
topology,
|
|
666
|
+
pathHints,
|
|
667
|
+
continuity: hasContinuationSignal ? "continue_current_transaction" : "new_transaction"
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (scores.agent >= scores.assistant && scores.agent > 0) {
|
|
672
|
+
return {
|
|
673
|
+
mode: "agent",
|
|
674
|
+
confidence: scores.agent >= 6 ? "high" : scores.agent >= 3 ? "medium" : "low",
|
|
675
|
+
reason: reasons.agent,
|
|
676
|
+
evidence,
|
|
677
|
+
topology,
|
|
678
|
+
pathHints,
|
|
679
|
+
continuity: hasContinuationSignal ? "continue_current_transaction" : "new_transaction"
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (scores.assistant > 0) {
|
|
684
|
+
return {
|
|
685
|
+
mode: "assistant",
|
|
686
|
+
confidence: scores.assistant >= 6 ? "high" : scores.assistant >= 3 ? "medium" : "low",
|
|
687
|
+
reason: reasons.assistant,
|
|
688
|
+
evidence,
|
|
689
|
+
topology,
|
|
690
|
+
pathHints,
|
|
691
|
+
continuity: hasContinuationSignal ? "continue_current_transaction" : "new_transaction"
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
mode: "assistant",
|
|
697
|
+
confidence: scores.assistant >= 6 ? "high" : scores.assistant >= 3 ? "medium" : "low",
|
|
698
|
+
reason: reasons.assistant || "default_assistant",
|
|
699
|
+
evidence,
|
|
700
|
+
topology,
|
|
701
|
+
pathHints,
|
|
702
|
+
continuity: hasContinuationSignal ? "continue_current_transaction" : "new_transaction"
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ========== Task 4: 前端任务检测与设计风格提示词 ==========
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* 检测 prompt 是否涉及前端/UI 任务
|
|
710
|
+
*/
|
|
711
|
+
export function detectFrontendTask(prompt) {
|
|
712
|
+
const lower = String(prompt || "").toLowerCase()
|
|
713
|
+
const frontendPatterns = [
|
|
714
|
+
/\b(react|vue|angular|svelte|next\.?js|nuxt|remix|astro|solid)\b/i,
|
|
715
|
+
/\b(html|css|scss|sass|less|tailwind|bootstrap|styled.components|emotion|chakra)\b/i,
|
|
716
|
+
/\b(ui|ux|frontend|front.end|web app|webpage|landing page|dashboard|component|widget)\b/i,
|
|
717
|
+
/\b(button|form|modal|navbar|sidebar|layout|grid|flex|animation|transition|responsive)\b/i,
|
|
718
|
+
/\b(前端|界面|页面|组件|样式|布局|动画|交互|响应式|移动端)\b/i
|
|
719
|
+
]
|
|
720
|
+
return frontendPatterns.some(re => re.test(lower))
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* 生成前端设计风格提示词块
|
|
725
|
+
* @param {string} designStyle - 用户 profile 中的 design_style
|
|
726
|
+
*/
|
|
727
|
+
export function buildFrontendDesignPrompt(designStyle = "") {
|
|
728
|
+
const lines = [
|
|
729
|
+
"## Frontend Design Guidelines",
|
|
730
|
+
"",
|
|
731
|
+
"Apply these principles to all UI/frontend code:",
|
|
732
|
+
"- Use semantic HTML5 elements (header, nav, main, section, article, footer)",
|
|
733
|
+
"- Responsive design with mobile-first approach (breakpoints: 640/768/1024/1280px)",
|
|
734
|
+
"- Accessibility: aria labels, keyboard navigation, color contrast ≥ 4.5:1",
|
|
735
|
+
"- CSS custom properties for theming; prefer CSS Grid/Flexbox for layouts",
|
|
736
|
+
"- Smooth transitions (150-300ms ease) for interactive elements",
|
|
737
|
+
"- Consistent spacing scale (4px base unit: 4/8/12/16/24/32/48/64px)",
|
|
738
|
+
"- Cross-browser compatibility (Chrome, Firefox, Safari, Edge)"
|
|
739
|
+
]
|
|
740
|
+
|
|
741
|
+
if (designStyle) {
|
|
742
|
+
const s = designStyle.toLowerCase()
|
|
743
|
+
if (s.includes("minimal") || s.includes("clean")) {
|
|
744
|
+
lines.push("- Style: Minimal/Clean — generous whitespace, 2-3 color palette, flat design, no decorative elements")
|
|
745
|
+
} else if (s.includes("material")) {
|
|
746
|
+
lines.push("- Style: Material Design — elevation shadows, ripple effects, Material color system, 8dp grid")
|
|
747
|
+
} else if (s.includes("dark")) {
|
|
748
|
+
lines.push("- Style: Dark theme — backgrounds #121212/#1e1e1e, surface #2d2d2d, ensure contrast ≥ 4.5:1")
|
|
749
|
+
} else if (s.includes("glass") || s.includes("glassmorphism")) {
|
|
750
|
+
lines.push("- Style: Glassmorphism — backdrop-filter blur(10-20px), semi-transparent bg (rgba white/black 0.1-0.2), subtle 1px border")
|
|
751
|
+
} else if (s.includes("neumorphism") || s.includes("soft")) {
|
|
752
|
+
lines.push("- Style: Neumorphism — soft inset/outset shadows, monochromatic palette, subtle depth without harsh borders")
|
|
753
|
+
} else {
|
|
754
|
+
lines.push(`- Style: ${designStyle} — apply consistently across all components`)
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return lines.join("\n")
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// ========== Phase 11: 恢复建议生成 ==========
|
|
762
|
+
|
|
763
|
+
export function generateRecoverySuggestions({ status, taskProgress, gateStatus, phase, recoveryCount, fileChanges }) {
|
|
764
|
+
const suggestions = []
|
|
765
|
+
|
|
766
|
+
// 分析已完成和失败的 task
|
|
767
|
+
const completedTasks = []
|
|
768
|
+
const failedTasks = []
|
|
769
|
+
if (taskProgress && typeof taskProgress === "object") {
|
|
770
|
+
for (const [taskId, tp] of Object.entries(taskProgress)) {
|
|
771
|
+
if (tp.status === "completed") {
|
|
772
|
+
completedTasks.push(taskId)
|
|
773
|
+
} else if (tp.status === "error" || tp.status === "cancelled") {
|
|
774
|
+
const category = classifyError(tp.lastError)
|
|
775
|
+
failedTasks.push({
|
|
776
|
+
taskId,
|
|
777
|
+
error: (tp.lastError || "").slice(0, 200),
|
|
778
|
+
category,
|
|
779
|
+
suggestion: category === "permanent"
|
|
780
|
+
? "此错误不可自动恢复,需要手动检查配置或文件路径"
|
|
781
|
+
: category === "logic"
|
|
782
|
+
? "代码逻辑错误,建议检查相关文件的实现"
|
|
783
|
+
: "临时性错误,可尝试重新运行"
|
|
784
|
+
})
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// 分析 gate 失败
|
|
790
|
+
const manualSteps = []
|
|
791
|
+
if (gateStatus) {
|
|
792
|
+
for (const [gate, info] of Object.entries(gateStatus)) {
|
|
793
|
+
if (info?.status === "fail" || info?.status === "fixing") {
|
|
794
|
+
manualSteps.push(`检查 ${gate} gate 的失败原因: ${info.failures || info.reason || "unknown"}`)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 根据 phase 判断失败阶段
|
|
800
|
+
if (phase) {
|
|
801
|
+
if (phase.startsWith("H4")) suggestions.push("编码阶段未完成,可尝试缩小任务范围后重试")
|
|
802
|
+
if (phase.startsWith("H5")) suggestions.push("调试阶段未通过,建议手动检查测试输出")
|
|
803
|
+
if (phase.startsWith("H6")) suggestions.push("门控检查未通过,建议手动运行 build/test/lint 命令")
|
|
804
|
+
if (phase.startsWith("H7")) suggestions.push("Git 合并阶段出现问题,建议手动解决冲突")
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const resumeHint = completedTasks.length > 0
|
|
808
|
+
? `已完成 ${completedTasks.length} 个 task,可从 checkpoint 恢复继续`
|
|
809
|
+
: "无已完成的 task,建议重新开始"
|
|
810
|
+
|
|
811
|
+
const summary = [
|
|
812
|
+
`状态: ${status}`,
|
|
813
|
+
`阶段: ${phase || "unknown"}`,
|
|
814
|
+
`恢复次数: ${recoveryCount || 0}`,
|
|
815
|
+
`已完成: ${completedTasks.length} task(s)`,
|
|
816
|
+
`失败: ${failedTasks.length} task(s)`,
|
|
817
|
+
`文件变更: ${fileChanges?.length || 0}`
|
|
818
|
+
].join(", ")
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
suggestions,
|
|
822
|
+
completedTasks,
|
|
823
|
+
failedTasks,
|
|
824
|
+
manualSteps,
|
|
825
|
+
resumeHint,
|
|
826
|
+
summary
|
|
827
|
+
}
|
|
828
|
+
}
|