magic-agent 2.1.5 → 2.3.2

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 (78) hide show
  1. package/CLIENT-CONTRACT.md +147 -11
  2. package/README.md +10 -4
  3. package/client-ui/pc/app.css +17 -0
  4. package/client-ui/pc/index.html +5 -0
  5. package/client-ui/pc/js/05-task-list-render.js +28 -4
  6. package/client-ui/pc/js/07-chat-stream-render.js +19 -8
  7. package/client-ui/pc/js/93-rail.js +24 -2
  8. package/client-ui/shared/conv/03-chat-message.js +63 -3
  9. package/client-ui/shared/conv/06-ask-answer.js +30 -7
  10. package/client-ui/shared/conv/09-stream-apply.js +16 -0
  11. package/client-ui/shared/conv/19-attachments.js +75 -11
  12. package/client-ui/shared/conv/27-conv-ledger.js +1 -1
  13. package/client-ui/shared/conv/29-msg-variants.js +56 -3
  14. package/client-ui/shared/conv/README.md +6 -0
  15. package/client-ui/shared/conv/conv.css +33 -1
  16. package/client-ui/shared/conversation.css +42 -4
  17. package/client-ui/shared/conversation.js +801 -102
  18. package/client-ui/shared/transport-http.js +199 -38
  19. package/client-ui-mobile/index.html +731 -581
  20. package/client-ui-mobile/reqs.css +39 -0
  21. package/client-ui-mobile/reqs.js +520 -0
  22. package/cmd/magic-client/main.go +1 -1
  23. package/internal/agent/capability.go +8 -1
  24. package/internal/agent/claude.go +5 -3
  25. package/internal/agent/codebuddy.go +6 -2
  26. package/internal/agent/codex.go +7 -2
  27. package/internal/agent/dsh_sdk.go +8 -0
  28. package/internal/agent/dsh_sdk_test.go +17 -0
  29. package/internal/agent/engine.go +20 -0
  30. package/internal/agent/engine_test.go +52 -0
  31. package/internal/agent/llmengine.go +14 -0
  32. package/internal/agent/llmengine_test.go +24 -0
  33. package/internal/agent/llmhttp.go +6 -0
  34. package/internal/agent/llmhttp_test.go +27 -0
  35. package/internal/agent/llmmodels.go +48 -0
  36. package/internal/agent/permission.go +5 -0
  37. package/internal/cli/ask.go +38 -17
  38. package/internal/cli/requirements.go +2 -0
  39. package/internal/cli/root.go +5 -0
  40. package/internal/cli/session.go +16 -14
  41. package/internal/cli/sessionlog.go +5 -0
  42. package/internal/cli/sessionlog_user_test.go +49 -0
  43. package/internal/cli/suggest.go +98 -0
  44. package/internal/cli/suggest_test.go +54 -0
  45. package/internal/cli/team.go +111 -24
  46. package/internal/client/requirements.go +27 -7
  47. package/internal/client/requirements_test.go +51 -0
  48. package/internal/client/service.go +35 -3
  49. package/internal/client/service_test.go +35 -0
  50. package/internal/client/suggest.go +131 -0
  51. package/internal/client/suggest_test.go +116 -0
  52. package/internal/requirements/consolidate.go +86 -24
  53. package/internal/requirements/requirements.go +9 -0
  54. package/internal/requirements/requirements_test.go +129 -0
  55. package/internal/requirements/store.go +11 -3
  56. package/internal/session/delete.go +58 -11
  57. package/internal/session/delete_test.go +140 -0
  58. package/internal/session/session.go +25 -0
  59. package/internal/session/session_mark_test.go +62 -0
  60. package/internal/suggest/suggest.go +532 -0
  61. package/internal/suggest/suggest_test.go +268 -0
  62. package/internal/team/board.go +107 -15
  63. package/internal/team/execute.go +1 -1
  64. package/internal/team/team.go +28 -19
  65. package/internal/team/team_test.go +68 -15
  66. package/npm/apk/magic-agent-mobile-release.apk +0 -0
  67. package/npm/apk/version.json +3 -3
  68. package/npm/dist/darwin-arm64/magic-agent +0 -0
  69. package/npm/dist/darwin-arm64/magic-client +0 -0
  70. package/npm/dist/darwin-x64/magic-agent +0 -0
  71. package/npm/dist/darwin-x64/magic-client +0 -0
  72. package/npm/dist/linux-arm64/magic-agent +0 -0
  73. package/npm/dist/linux-arm64/magic-client +0 -0
  74. package/npm/dist/linux-x64/magic-agent +0 -0
  75. package/npm/dist/linux-x64/magic-client +0 -0
  76. package/npm/dist/win32-x64/magic-agent.exe +0 -0
  77. package/npm/dist/win32-x64/magic-client.exe +0 -0
  78. package/package.json +6 -2
@@ -1323,31 +1323,50 @@ func TestWriteOutputRecordsMissing(t *testing.T) {
1323
1323
  }
1324
1324
  }
1325
1325
 
1326
- // 执行阶段心跳:只播报「还在跑且很久没动静」的任务,收尾的不再播。
1327
- func TestBoardQuietLinesOnlyAnnouncesSilentRunningTasks(t *testing.T) {
1328
- b := newBoard(func(agent.StreamEvent) {})
1326
+ // 执行阶段心跳:只给「还在跑且很久没动静」的那一行挂行内进度,收尾的不再挂。
1327
+ //
1328
+ // 2026-10-22 用户:「子任务进度也不需要显示多行 在任务列表中显示即可」——
1329
+ // 心跳落点从一串 notice 改成任务列表行的 Active,所以断言也看行。
1330
+ func TestBoardQuietOnlyMarksSilentRunningTasks(t *testing.T) {
1331
+ var events []agent.StreamEvent
1332
+ b := newBoard(func(ev agent.StreamEvent) { events = append(events, ev) })
1329
1333
  t1 := Task{ID: "t1", Kind: KindText, Title: "写介绍"}
1330
1334
  t2 := Task{ID: "t2", Kind: KindImage, Title: "画封面"}
1331
1335
  t3 := Task{ID: "t3", Kind: KindText, Title: "汇总图"}
1332
1336
  for _, task := range []Task{t1, t2, t3} {
1333
1337
  b.add(task, Route{})
1334
1338
  }
1339
+ b.announce()
1340
+ events = nil
1335
1341
  b.start(t1, "e", "")
1336
1342
  b.start(t2, "e", "")
1337
1343
  b.finish(t3, TaskResult{ID: "t3", State: TaskDone})
1344
+ events = nil
1338
1345
 
1339
1346
  // t1 刚刚有输出;t3 已收尾;t2 静默超过阈值。
1340
- b.touch("t1")
1347
+ b.touch("t1", agent.StreamEvent{Kind: agent.KindText})
1341
1348
  b.mu.Lock()
1342
1349
  b.last["t2"] = time.Now().Add(-time.Minute)
1343
1350
  b.mu.Unlock()
1344
1351
 
1345
- got := b.quietLines()
1346
- if len(got) != 1 || !strings.Contains(got[0], "t2") || !strings.Contains(got[0], "画封面") {
1347
- t.Fatalf("只该播报静默中的 t2,得到 %v", got)
1352
+ b.quiet()
1353
+ if len(events) != 1 || events[0].Kind != agent.KindTasks {
1354
+ t.Fatalf("心跳应当整份重发一次任务列表快照,得到 %d 条 %v", len(events), events)
1355
+ }
1356
+ if !strings.Contains(events[0].Text, "无新输出") {
1357
+ t.Fatalf("快照里应当带上这一行的行内进度:%s", events[0].Text)
1358
+ }
1359
+ b.mu.Lock()
1360
+ active1, active2, active3 := b.rows["t1"].Active, b.rows["t2"].Active, b.rows["t3"].Active
1361
+ b.mu.Unlock()
1362
+ if active2 == "" || active1 != "" || active3 != "" {
1363
+ t.Fatalf("只该给静默中的 t2 挂进度,得到 t1=%q t2=%q t3=%q", active1, active2, active3)
1348
1364
  }
1349
- if again := b.quietLines(); len(again) != 0 {
1350
- t.Fatalf("刚播过的任务不应立刻重复:%v", again)
1365
+
1366
+ events = nil
1367
+ b.quiet()
1368
+ if len(events) != 0 {
1369
+ t.Fatalf("刚刷过的行不应立刻重复发快照:%v", events)
1351
1370
  }
1352
1371
  }
1353
1372
 
@@ -1363,24 +1382,58 @@ func TestBoardTouchKeepsRunningRowAlive(t *testing.T) {
1363
1382
  b.last["t2"] = time.Now().Add(-time.Minute)
1364
1383
  b.mu.Unlock()
1365
1384
 
1366
- b.touch("t2")
1367
- if got := b.quietLines(); len(got) != 0 {
1368
- t.Fatalf("刚有动静的任务不该被心跳播报:%v", got)
1369
- }
1385
+ b.touch("t2", agent.StreamEvent{Kind: agent.KindText})
1386
+ b.quiet()
1370
1387
  b.mu.Lock()
1371
1388
  row := b.rows["t2"]
1372
1389
  b.mu.Unlock()
1373
1390
  if row == nil || row.Status != RowRunning {
1374
1391
  t.Fatalf("内部事件不该改变这一行的状态,得到 %+v", row)
1375
1392
  }
1393
+ if row.Active != "" {
1394
+ t.Fatalf("刚有动静的任务不该挂着「无新输出」:%q", row.Active)
1395
+ }
1376
1396
 
1377
1397
  // 真正的收尾由 finish 落定(之后不再进心跳候选)。
1378
1398
  b.finish(t2, TaskResult{ID: "t2", State: TaskDone})
1379
1399
  b.mu.Lock()
1380
1400
  b.last["t2"] = time.Now().Add(-time.Minute)
1381
1401
  b.mu.Unlock()
1382
- if got := b.quietLines(); len(got) != 0 {
1383
- t.Fatalf("已收尾的任务不该被心跳播报:%v", got)
1402
+ b.quiet()
1403
+ b.mu.Lock()
1404
+ row = b.rows["t2"]
1405
+ b.mu.Unlock()
1406
+ if row.Active != "" {
1407
+ t.Fatalf("已收尾的任务不该被心跳挂进度:%q", row.Active)
1408
+ }
1409
+ }
1410
+
1411
+ // 卡在授权上的子任务:行内进度必须说「等待授权」,而不是「已 N 秒无新输出」。
1412
+ // 回归:2026-10-22 实测 --team-permission accept-edits 下子引擎读任务书也要授权,
1413
+ // 两个子任务一起停在那里十几分钟,主会话里没有任何一处看得出来它在等人。
1414
+ func TestBoardTouchShowsPendingAsk(t *testing.T) {
1415
+ var events []agent.StreamEvent
1416
+ b := newBoard(func(ev agent.StreamEvent) { events = append(events, ev) })
1417
+ t1 := Task{ID: "t1", Kind: KindText, Title: "写介绍"}
1418
+ b.add(t1, Route{})
1419
+ b.start(t1, "e", "")
1420
+ events = nil
1421
+
1422
+ b.touch("t1", agent.StreamEvent{Kind: agent.KindAsk, Text: "需要授权:Read(等待用户选择)"})
1423
+ if len(events) != 1 || events[0].Kind != agent.KindTasks {
1424
+ t.Fatalf("卡在授权上要当场重发一次快照,得到 %d 条", len(events))
1425
+ }
1426
+ if !strings.Contains(events[0].Text, "等待用户选择") {
1427
+ t.Fatalf("快照里应当带着等待授权的说明:%s", events[0].Text)
1428
+ }
1429
+
1430
+ // 有后续输出(用户放行 / 引擎自己继续)→ 行内进度清空。
1431
+ b.touch("t1", agent.StreamEvent{Kind: agent.KindText, Text: "继续"})
1432
+ b.mu.Lock()
1433
+ active := b.rows["t1"].Active
1434
+ b.mu.Unlock()
1435
+ if active != "" {
1436
+ t.Fatalf("恢复输出后不该还挂着等待授权:%q", active)
1384
1437
  }
1385
1438
  }
1386
1439
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.1.3",
3
- "versionCode": 20103,
4
- "builtAt": "2026-10-22T11:18:59.511Z"
2
+ "version": "2.3.2",
3
+ "versionCode": 20302,
4
+ "builtAt": "2026-10-23T03:26:46.892Z"
5
5
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magic-agent",
3
- "version": "2.1.5",
3
+ "version": "2.3.2",
4
4
  "description": "专业的 agent CLI 代理工具 - 统一 claude / codebuddy / trae / llm / codex / openclaw 引擎",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -14,9 +14,13 @@
14
14
  "prepack": "node npm/build.js",
15
15
  "postinstall": "node npm/install.js",
16
16
  "check:ui": "node tests/check-ui-spec.mjs",
17
+ "check:pin": "node tests/check-task-pin.mjs",
17
18
  "check:conv": "node tests/check-conv-shards.mjs",
18
19
  "check:conv-host": "node tests/check-conv-shared-host.mjs",
19
- "check:conv-comp": "node tests/check-conv-component.mjs"
20
+ "check:conv-mobile": "node tests/check-conv-mobile-host.mjs",
21
+ "check:conv-comp": "node tests/check-conv-component.mjs",
22
+ "check:conv-queue": "node tests/check-conv-queue.mjs",
23
+ "check:follow": "node tests/check-follow-retry.mjs"
20
24
  },
21
25
  "files": [
22
26
  "npm/bin/",