magic-agent 2.4.3 → 2.6.0

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 (153) hide show
  1. package/CLIENT-CONTRACT.md +284 -30
  2. package/README.md +15 -14
  3. package/client-ui/pc/app.css +32 -0
  4. package/client-ui/pc/js/04-task-list.js +5 -1
  5. package/client-ui/pc/js/07-chat-stream-render.js +3 -3
  6. package/client-ui/pc/js/90-glue.js +37 -2
  7. package/client-ui/pc/js/92-composer.js +19 -4
  8. package/client-ui/pc/js/93-rail.js +16 -5
  9. package/client-ui/shared/conv/02-media-kinds.js +2 -2
  10. package/client-ui/shared/conv/03-chat-message.js +241 -23
  11. package/client-ui/shared/conv/06-ask-answer.js +75 -1
  12. package/client-ui/shared/conv/09-stream-apply.js +63 -7
  13. package/client-ui/shared/conv/19-attachments.js +157 -6
  14. package/client-ui/shared/conv/27-conv-ledger.js +5 -2
  15. package/client-ui/shared/conv/29-msg-variants.js +81 -0
  16. package/client-ui/shared/conv/conv.css +79 -19
  17. package/client-ui/shared/conv/dom-map.md +1 -0
  18. package/client-ui/shared/conversation.css +13 -2
  19. package/client-ui/shared/conversation.js +336 -50
  20. package/client-ui/shared/transport-http.js +14 -0
  21. package/client-ui-mobile/index.html +2932 -333
  22. package/client-ui-mobile/reqs.css +102 -0
  23. package/client-ui-mobile/reqs.js +485 -70
  24. package/client-ui-mobile/reqshot.js +505 -0
  25. package/client-ui-mobile/scripts.css +21 -0
  26. package/client-ui-mobile/scripts.js +539 -0
  27. package/client-ui-mobile/vendor/cose-base.js +3214 -0
  28. package/client-ui-mobile/vendor/cytoscape-fcose.js +1549 -0
  29. package/client-ui-mobile/vendor/cytoscape.min.js +32 -0
  30. package/client-ui-mobile/vendor/layout-base.js +5230 -0
  31. package/cmd/magic-client/browsectl.go +199 -0
  32. package/cmd/magic-client/clientctl.go +33 -9
  33. package/cmd/magic-client/main.go +57 -26
  34. package/internal/agent/ask.go +45 -0
  35. package/internal/agent/ask_test.go +39 -0
  36. package/internal/agent/attachments_test.go +6 -2
  37. package/internal/agent/capability.go +7 -1
  38. package/internal/agent/claude.go +20 -15
  39. package/internal/agent/codebuddy.go +125 -34
  40. package/internal/agent/codebuddy_account_test.go +54 -0
  41. package/internal/agent/codebuddy_stream_test.go +103 -0
  42. package/internal/agent/credits.go +713 -0
  43. package/internal/agent/credits_minimax_test.go +97 -0
  44. package/internal/agent/credits_test.go +211 -0
  45. package/internal/agent/custom_models.go +155 -0
  46. package/internal/agent/custom_models_test.go +125 -0
  47. package/internal/agent/dsh.go +123 -49
  48. package/internal/agent/dsh_resume.go +13 -2
  49. package/internal/agent/dsh_test.go +108 -6
  50. package/internal/agent/engine.go +37 -1
  51. package/internal/agent/engine_base.go +21 -17
  52. package/internal/agent/engine_base_test.go +2 -2
  53. package/internal/agent/engine_test.go +47 -86
  54. package/internal/agent/fallback.go +313 -0
  55. package/internal/agent/fallback_test.go +340 -0
  56. package/internal/agent/launch.go +43 -1
  57. package/internal/agent/llmhttp.go +46 -4
  58. package/internal/agent/llmhttp_test.go +157 -0
  59. package/internal/agent/models.go +53 -1
  60. package/internal/agent/models_test.go +20 -5
  61. package/internal/agent/pidalive_windows_test.go +40 -0
  62. package/internal/agent/runcmd.go +26 -0
  63. package/internal/agent/runcmd_windows.go +36 -5
  64. package/internal/agent/stream.go +136 -6
  65. package/internal/agent/stream_test.go +25 -38
  66. package/internal/agent/stream_timeline_test.go +2 -37
  67. package/internal/agent/streamjson.go +108 -6
  68. package/internal/agent/tools_suffix_test.go +6 -3
  69. package/internal/agent/trae.go +50 -243
  70. package/internal/agent/trae_exec.go +433 -0
  71. package/internal/agent/trae_exec_test.go +181 -0
  72. package/internal/agent/wraplaunch_test.go +113 -0
  73. package/internal/cli/ask.go +160 -37
  74. package/internal/cli/ask_test.go +1 -1
  75. package/internal/cli/checkin.go +77 -0
  76. package/internal/cli/clientcmd.go +3 -2
  77. package/internal/cli/control.go +46 -5
  78. package/internal/cli/control_test.go +65 -0
  79. package/internal/cli/events.go +4 -0
  80. package/internal/cli/quotas.go +125 -0
  81. package/internal/cli/requirements.go +51 -0
  82. package/internal/cli/root.go +122 -5
  83. package/internal/cli/session.go +6 -0
  84. package/internal/cli/suggest.go +7 -5
  85. package/internal/cli/team.go +37 -9
  86. package/internal/cli/team_test.go +39 -0
  87. package/internal/client/ab.go +372 -0
  88. package/internal/client/ab_test.go +287 -0
  89. package/internal/client/browse.go +84 -36
  90. package/internal/client/browse_test.go +107 -9
  91. package/internal/client/browseinput.go +481 -0
  92. package/internal/client/browseinput_test.go +216 -0
  93. package/internal/client/corecmd.go +23 -0
  94. package/internal/client/corecmd_other.go +8 -0
  95. package/internal/client/corecmd_windows.go +24 -0
  96. package/internal/client/daemon.go +90 -17
  97. package/internal/client/daemon_test.go +33 -0
  98. package/internal/client/engines_checkin_test.go +57 -0
  99. package/internal/client/fallback_flags_test.go +64 -0
  100. package/internal/client/jobs.go +294 -21
  101. package/internal/client/jobs_test.go +299 -2
  102. package/internal/client/knowledge.go +854 -0
  103. package/internal/client/knowledge_test.go +510 -0
  104. package/internal/client/lan.go +3 -3
  105. package/internal/client/quotas_merge_test.go +107 -0
  106. package/internal/client/requirements.go +198 -1
  107. package/internal/client/requirements_shots_test.go +200 -0
  108. package/internal/client/restart.go +134 -0
  109. package/internal/client/restart_test.go +151 -0
  110. package/internal/client/scripts.go +1047 -0
  111. package/internal/client/scripts_decode_other.go +27 -0
  112. package/internal/client/scripts_decode_test.go +125 -0
  113. package/internal/client/scripts_decode_windows.go +206 -0
  114. package/internal/client/scripts_test.go +726 -0
  115. package/internal/client/service.go +322 -18
  116. package/internal/client/service_test.go +51 -0
  117. package/internal/client/suggest.go +105 -2
  118. package/internal/client/suggest_test.go +86 -0
  119. package/internal/client/team_flags.go +25 -11
  120. package/internal/client/team_flags_test.go +39 -0
  121. package/internal/requirements/bootstrap.go +16 -0
  122. package/internal/requirements/consolidate.go +100 -9
  123. package/internal/requirements/doc.go +154 -3
  124. package/internal/requirements/graph.go +138 -0
  125. package/internal/requirements/memory.go +7 -0
  126. package/internal/requirements/modules.go +312 -0
  127. package/internal/requirements/modules_test.go +373 -0
  128. package/internal/requirements/relations.go +155 -0
  129. package/internal/requirements/relations_test.go +231 -0
  130. package/internal/requirements/requirements.go +10 -0
  131. package/internal/requirements/shots.go +418 -0
  132. package/internal/requirements/shots_test.go +387 -0
  133. package/internal/session/session.go +92 -0
  134. package/internal/suggest/suggest.go +23 -1
  135. package/internal/team/event.go +3 -1
  136. package/internal/team/execute.go +21 -6
  137. package/internal/team/fallback_route_test.go +80 -0
  138. package/internal/team/planner.go +15 -3
  139. package/internal/team/route.go +89 -17
  140. package/internal/team/team.go +18 -4
  141. package/npm/apk/magic-agent-mobile-release.apk +0 -0
  142. package/npm/apk/version.json +1 -5
  143. package/npm/dist/darwin-arm64/magic-agent +0 -0
  144. package/npm/dist/darwin-arm64/magic-client +0 -0
  145. package/npm/dist/darwin-x64/magic-agent +0 -0
  146. package/npm/dist/darwin-x64/magic-client +0 -0
  147. package/npm/dist/linux-arm64/magic-agent +0 -0
  148. package/npm/dist/linux-arm64/magic-client +0 -0
  149. package/npm/dist/linux-x64/magic-agent +0 -0
  150. package/npm/dist/linux-x64/magic-client +0 -0
  151. package/npm/dist/win32-x64/magic-agent.exe +0 -0
  152. package/npm/dist/win32-x64/magic-client.exe +0 -0
  153. package/package.json +8 -2
@@ -0,0 +1,151 @@
1
+ package client
2
+
3
+ import (
4
+ "encoding/json"
5
+ "io"
6
+ "log"
7
+ "net/http"
8
+ "net/http/httptest"
9
+ "os"
10
+ "path/filepath"
11
+ "testing"
12
+ "time"
13
+ )
14
+
15
+ /*
16
+ 重启链路(2026-10-25 用户:「点击重启服务时标志一下 然后定时器触发一下」):
17
+
18
+ 先立标志(重复点击不再起第二个助手)→ 先答 200 → 定时器到点才拉助手。
19
+ 助手本身用假动作替身,不碰真进程。
20
+ */
21
+ func TestHandleRestartFlagsThenTimer(t *testing.T) {
22
+ dir := t.TempDir()
23
+ statePath := filepath.Join(dir, "client.json")
24
+ t.Setenv(EnvState, statePath)
25
+ exe, err := os.Executable()
26
+ if err != nil {
27
+ t.Fatal(err)
28
+ }
29
+ // 状态文件指向**本进程**:过 handleRestart 那道「不许重启别人」的闸。
30
+ if err := writeDaemonState(statePath, &DaemonState{
31
+ PID: os.Getpid(), Port: 12345, Token: "tok", Exe: exe, Lan: true,
32
+ }); err != nil {
33
+ t.Fatal(err)
34
+ }
35
+
36
+ // 假助手:只记录被拉起过几次、带的是什么可执行文件。
37
+ spawned := make(chan string, 4)
38
+ oldSpawner := restartSpawner
39
+ restartSpawner = func(gotExe string, _ *log.Logger) { spawned <- gotExe }
40
+ defer func() { restartSpawner = oldSpawner }()
41
+ // 标志是进程级的:用例前后都重置,别把别的用例立过的 pending 带进来。
42
+ resetRestartPendingForTest()
43
+ defer resetRestartPendingForTest()
44
+ // 定时器等短一点:用例不真等一秒(仍要足够长,长过「返回即查」那一瞬)。
45
+ oldDelay := restartDelay
46
+ restartDelay = 100 * time.Millisecond
47
+ defer func() { restartDelay = oldDelay }()
48
+
49
+ s := &Service{log: log.New(io.Discard, "", 0)}
50
+
51
+ // 第一次点击:200 + restarting,且**定时器到点前**助手不许已被拉起
52
+ // (响应落地必须严格早于助手起身)。
53
+ rec := httptest.NewRecorder()
54
+ s.handleRestart(rec, httptest.NewRequest(http.MethodPost, "/desk/restart", nil))
55
+ if rec.Code != http.StatusOK {
56
+ t.Fatalf("POST /desk/restart = %d,want 200(body: %s)", rec.Code, rec.Body.String())
57
+ }
58
+ var body map[string]any
59
+ if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
60
+ t.Fatalf("解析响应失败: %v(%s)", err, rec.Body.String())
61
+ }
62
+ if body["restarting"] != true {
63
+ t.Errorf("响应应带 restarting=true,实际 %v", body["restarting"])
64
+ }
65
+ select {
66
+ case <-spawned:
67
+ t.Fatal("定时器到点前就拉了助手:响应可能还没落地")
68
+ default:
69
+ }
70
+
71
+ // 重复点击:认账(already_pending)但不再排第二个定时器。
72
+ rec = httptest.NewRecorder()
73
+ s.handleRestart(rec, httptest.NewRequest(http.MethodPost, "/desk/restart", nil))
74
+ if rec.Code != http.StatusOK {
75
+ t.Fatalf("重复点击 = %d,want 200", rec.Code)
76
+ }
77
+ body = nil
78
+ if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
79
+ t.Fatalf("解析重复点击响应失败: %v", err)
80
+ }
81
+ if body["already_pending"] != true {
82
+ t.Errorf("重复点击应答 already_pending=true,实际 %v", body["already_pending"])
83
+ }
84
+
85
+ // 定时器到点:助手被拉起一次,带的是本进程的可执行文件。
86
+ select {
87
+ case got := <-spawned:
88
+ if got != exe {
89
+ t.Errorf("助手可执行文件 = %q,want %q", got, exe)
90
+ }
91
+ case <-time.After(2 * time.Second):
92
+ t.Fatal("定时器没有拉起助手")
93
+ }
94
+ // 只有一次:重复点击没有多起助手(两个助手会互相停掉对方刚拉起的实例)。
95
+ select {
96
+ case <-spawned:
97
+ t.Fatal("重复点击多拉起了一个助手")
98
+ default:
99
+ }
100
+ }
101
+
102
+ /*
103
+ /desk/health 要带出重启标志:手机据此把「旧实例认账」当成「它马上要下线」,
104
+
105
+ 不必等连接真的断;再探到不带标志的 200 才是新一代。
106
+ */
107
+ func TestHandleHealthReportsRestarting(t *testing.T) {
108
+ // 直接搭一个最小 Service(不走 New():那个要 exec core 契约,Windows 上跑不了)。
109
+ // Token 留空 → guard 放行、publicURL 的探测直接短路;Storage.Dir 指临时目录,
110
+ // instanceID 不会去写用户目录。
111
+ prof := DefaultProfile()
112
+ prof.Storage.Dir = t.TempDir()
113
+ svc := &Service{
114
+ log: log.New(io.Discard, "", 0),
115
+ contract: Contract{ContractVersion: 2, Version: "1.0.103"},
116
+ opts: Options{Profile: prof},
117
+ }
118
+ restarting := func() bool {
119
+ rec := httptest.NewRecorder()
120
+ svc.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/desk/health", nil))
121
+ if rec.Code != http.StatusOK {
122
+ t.Fatalf("health = %d,want 200(body: %s)", rec.Code, rec.Body.String())
123
+ }
124
+ var got struct {
125
+ Restarting bool `json:"restarting"`
126
+ }
127
+ if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
128
+ t.Fatalf("解析 health 失败: %v", err)
129
+ }
130
+ return got.Restarting
131
+ }
132
+
133
+ resetRestartPendingForTest()
134
+ defer resetRestartPendingForTest()
135
+ if restarting() {
136
+ t.Fatal("没受理重启时 health.restarting 应为 false")
137
+ }
138
+ restartMu.Lock()
139
+ restartPending = true
140
+ restartMu.Unlock()
141
+ if !restarting() {
142
+ t.Error("受理重启后 health.restarting 应为 true(旧实例认账,即将下线)")
143
+ }
144
+ }
145
+
146
+ // resetRestartPendingForTest 清掉重启标志(仅供测试:标志是进程级的,用例之间要重置)。
147
+ func resetRestartPendingForTest() {
148
+ restartMu.Lock()
149
+ restartPending = false
150
+ restartMu.Unlock()
151
+ }