magic-agent 2.4.3 → 2.6.3

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 (207) hide show
  1. package/CLIENT-CONTRACT.md +467 -41
  2. package/README.md +15 -14
  3. package/client-ui/pc/app.css +74 -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 +40 -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 +247 -23
  11. package/client-ui/shared/conv/06-ask-answer.js +75 -1
  12. package/client-ui/shared/conv/09-stream-apply.js +72 -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 +217 -0
  16. package/client-ui/shared/conv/conv.css +329 -174
  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 +373 -50
  20. package/client-ui/shared/transport-http.js +14 -0
  21. package/client-ui-mobile/apps.css +16 -0
  22. package/client-ui-mobile/apps.js +406 -0
  23. package/client-ui-mobile/index.html +5554 -639
  24. package/client-ui-mobile/reqs.css +110 -1
  25. package/client-ui-mobile/reqs.js +533 -74
  26. package/client-ui-mobile/reqshot.js +505 -0
  27. package/client-ui-mobile/scripts.css +67 -0
  28. package/client-ui-mobile/scripts.js +599 -0
  29. package/client-ui-mobile/vendor/cose-base.js +3214 -0
  30. package/client-ui-mobile/vendor/cytoscape-fcose.js +1549 -0
  31. package/client-ui-mobile/vendor/cytoscape.min.js +32 -0
  32. package/client-ui-mobile/vendor/layout-base.js +5230 -0
  33. package/cmd/magic-client/browsectl.go +199 -0
  34. package/cmd/magic-client/clientctl.go +69 -10
  35. package/cmd/magic-client/main.go +210 -32
  36. package/internal/agent/ask.go +45 -0
  37. package/internal/agent/ask_test.go +39 -0
  38. package/internal/agent/attachments_test.go +6 -2
  39. package/internal/agent/capability.go +7 -1
  40. package/internal/agent/claude.go +20 -15
  41. package/internal/agent/codebuddy.go +125 -34
  42. package/internal/agent/codebuddy_account_test.go +54 -0
  43. package/internal/agent/codebuddy_stream_test.go +103 -0
  44. package/internal/agent/credits.go +713 -0
  45. package/internal/agent/credits_minimax_test.go +97 -0
  46. package/internal/agent/credits_test.go +211 -0
  47. package/internal/agent/custom_models.go +155 -0
  48. package/internal/agent/custom_models_test.go +125 -0
  49. package/internal/agent/dsh.go +123 -49
  50. package/internal/agent/dsh_caps_cache_test.go +184 -0
  51. package/internal/agent/dsh_resume.go +157 -7
  52. package/internal/agent/dsh_test.go +108 -6
  53. package/internal/agent/engine.go +37 -1
  54. package/internal/agent/engine_base.go +21 -17
  55. package/internal/agent/engine_base_test.go +2 -2
  56. package/internal/agent/engine_test.go +47 -86
  57. package/internal/agent/env.go +173 -1
  58. package/internal/agent/env_test.go +114 -0
  59. package/internal/agent/fallback.go +313 -0
  60. package/internal/agent/fallback_test.go +340 -0
  61. package/internal/agent/launch.go +43 -1
  62. package/internal/agent/limitnotice.go +207 -1
  63. package/internal/agent/limitnotice_test.go +140 -0
  64. package/internal/agent/llmhttp.go +46 -4
  65. package/internal/agent/llmhttp_test.go +157 -0
  66. package/internal/agent/models.go +53 -1
  67. package/internal/agent/models_test.go +20 -5
  68. package/internal/agent/pidalive_windows_test.go +40 -0
  69. package/internal/agent/runcmd.go +26 -0
  70. package/internal/agent/runcmd_windows.go +36 -5
  71. package/internal/agent/stream.go +166 -6
  72. package/internal/agent/stream_test.go +25 -38
  73. package/internal/agent/stream_timeline_test.go +2 -37
  74. package/internal/agent/streamjson.go +119 -7
  75. package/internal/agent/tools_suffix_test.go +6 -3
  76. package/internal/agent/trae.go +50 -243
  77. package/internal/agent/trae_exec.go +433 -0
  78. package/internal/agent/trae_exec_test.go +181 -0
  79. package/internal/agent/wraplaunch_test.go +113 -0
  80. package/internal/cli/ask.go +168 -37
  81. package/internal/cli/ask_test.go +1 -1
  82. package/internal/cli/checkin.go +77 -0
  83. package/internal/cli/clientcmd.go +3 -2
  84. package/internal/cli/control.go +46 -5
  85. package/internal/cli/control_test.go +65 -0
  86. package/internal/cli/engines_test.go +103 -0
  87. package/internal/cli/events.go +15 -0
  88. package/internal/cli/events_test.go +50 -0
  89. package/internal/cli/judge.go +104 -0
  90. package/internal/cli/quotas.go +125 -0
  91. package/internal/cli/requirements.go +51 -0
  92. package/internal/cli/root.go +157 -11
  93. package/internal/cli/session.go +75 -3
  94. package/internal/cli/session_test.go +82 -3
  95. package/internal/cli/sessionlog.go +6 -0
  96. package/internal/cli/sessionlog_test.go +22 -2
  97. package/internal/cli/suggest.go +7 -5
  98. package/internal/cli/team.go +37 -9
  99. package/internal/cli/team_test.go +39 -0
  100. package/internal/client/ab.go +372 -0
  101. package/internal/client/ab_test.go +287 -0
  102. package/internal/client/apk.go +8 -0
  103. package/internal/client/apk_test.go +59 -0
  104. package/internal/client/apps.go +1078 -0
  105. package/internal/client/apps_test.go +516 -0
  106. package/internal/client/browse.go +84 -36
  107. package/internal/client/browse_test.go +107 -9
  108. package/internal/client/browseinput.go +481 -0
  109. package/internal/client/browseinput_test.go +216 -0
  110. package/internal/client/corecmd.go +23 -0
  111. package/internal/client/corecmd_other.go +8 -0
  112. package/internal/client/corecmd_windows.go +24 -0
  113. package/internal/client/coreexe.go +196 -0
  114. package/internal/client/coreexe_test.go +161 -0
  115. package/internal/client/coreupgrade.go +866 -0
  116. package/internal/client/coreupgrade_test.go +561 -0
  117. package/internal/client/daemon.go +297 -33
  118. package/internal/client/daemon_test.go +33 -0
  119. package/internal/client/engines_checkin_test.go +57 -0
  120. package/internal/client/fallback_flags_test.go +64 -0
  121. package/internal/client/feishu.go +1259 -0
  122. package/internal/client/feishu_test.go +649 -0
  123. package/internal/client/health_identity_test.go +84 -0
  124. package/internal/client/hostenv.go +68 -0
  125. package/internal/client/hostenv_test.go +37 -0
  126. package/internal/client/jobs.go +518 -24
  127. package/internal/client/jobs_auto.go +571 -0
  128. package/internal/client/jobs_auto_test.go +431 -0
  129. package/internal/client/jobs_test.go +299 -2
  130. package/internal/client/knowledge.go +854 -0
  131. package/internal/client/knowledge_test.go +510 -0
  132. package/internal/client/lan.go +3 -3
  133. package/internal/client/plugins.go +5 -1
  134. package/internal/client/prefs.go +67 -0
  135. package/internal/client/procstart_linux.go +59 -0
  136. package/internal/client/publicurl.go +82 -0
  137. package/internal/client/quotas_merge_test.go +107 -0
  138. package/internal/client/requirements.go +207 -2
  139. package/internal/client/requirements_shots_test.go +200 -0
  140. package/internal/client/requirements_test.go +103 -0
  141. package/internal/client/restart.go +288 -0
  142. package/internal/client/restart_handoff_test.go +230 -0
  143. package/internal/client/restart_test.go +290 -0
  144. package/internal/client/scripts.go +1047 -0
  145. package/internal/client/scripts_decode_implausible.go +38 -0
  146. package/internal/client/scripts_decode_other.go +31 -0
  147. package/internal/client/scripts_decode_test.go +130 -0
  148. package/internal/client/scripts_decode_windows.go +182 -0
  149. package/internal/client/scripts_test.go +739 -0
  150. package/internal/client/service.go +717 -97
  151. package/internal/client/service_test.go +217 -1
  152. package/internal/client/sessions_cache.go +148 -0
  153. package/internal/client/sessions_cache_test.go +163 -0
  154. package/internal/client/sessions_fold.go +165 -0
  155. package/internal/client/sessions_fold_test.go +204 -0
  156. package/internal/client/singleinstance.go +470 -0
  157. package/internal/client/singleinstance_test.go +607 -0
  158. package/internal/client/suggest.go +105 -2
  159. package/internal/client/suggest_test.go +86 -0
  160. package/internal/client/team_flags.go +25 -11
  161. package/internal/client/team_flags_test.go +39 -0
  162. package/internal/client/testmain_test.go +88 -0
  163. package/internal/client/workspace_default_test.go +158 -0
  164. package/internal/client/workspacelockenv_test.go +54 -0
  165. package/internal/config/config.go +48 -0
  166. package/internal/judge/judge.go +315 -0
  167. package/internal/requirements/bootstrap.go +16 -0
  168. package/internal/requirements/consolidate.go +100 -9
  169. package/internal/requirements/doc.go +154 -3
  170. package/internal/requirements/graph.go +138 -0
  171. package/internal/requirements/memory.go +7 -0
  172. package/internal/requirements/modules.go +312 -0
  173. package/internal/requirements/modules_test.go +373 -0
  174. package/internal/requirements/relations.go +155 -0
  175. package/internal/requirements/relations_test.go +231 -0
  176. package/internal/requirements/requirements.go +10 -0
  177. package/internal/requirements/shots.go +418 -0
  178. package/internal/requirements/shots_test.go +387 -0
  179. package/internal/session/multiclient_test.go +368 -0
  180. package/internal/session/session.go +175 -11
  181. package/internal/session/session_test.go +7 -0
  182. package/internal/session/status.go +21 -0
  183. package/internal/session/status_test.go +28 -0
  184. package/internal/session/title.go +16 -0
  185. package/internal/session/title_test.go +26 -0
  186. package/internal/session/workspace_lock.go +265 -28
  187. package/internal/suggest/suggest.go +23 -1
  188. package/internal/team/event.go +3 -1
  189. package/internal/team/execute.go +21 -6
  190. package/internal/team/fallback_route_test.go +80 -0
  191. package/internal/team/planner.go +15 -3
  192. package/internal/team/route.go +89 -17
  193. package/internal/team/team.go +18 -4
  194. package/npm/apk/magic-agent-mobile-release.apk +0 -0
  195. package/npm/apk/version.json +6 -3
  196. package/npm/dist/darwin-arm64/magic-agent +0 -0
  197. package/npm/dist/darwin-arm64/magic-client +0 -0
  198. package/npm/dist/darwin-x64/magic-agent +0 -0
  199. package/npm/dist/darwin-x64/magic-client +0 -0
  200. package/npm/dist/linux-arm64/magic-agent +0 -0
  201. package/npm/dist/linux-arm64/magic-client +0 -0
  202. package/npm/dist/linux-x64/magic-agent +0 -0
  203. package/npm/dist/linux-x64/magic-client +0 -0
  204. package/npm/dist/win32-x64/magic-agent.exe +0 -0
  205. package/npm/dist/win32-x64/magic-client.exe +0 -0
  206. package/npm/install.js +11 -0
  207. package/package.json +14 -3
@@ -0,0 +1,84 @@
1
+ package client
2
+
3
+ import (
4
+ "encoding/json"
5
+ "net/http"
6
+ "net/http/httptest"
7
+ "os"
8
+ "testing"
9
+ "time"
10
+ )
11
+
12
+ /* /desk/health 必须交代「我这一代进程是谁」(2026-10-27)。
13
+ *
14
+ * 为什么值得一条测试:重启换的是**进程**,会话 / 任务 / 设置全在盘上,
15
+ * 界面重启前后长得一模一样。用户点了「重启服务」看见列表没变,只会说「没效果」
16
+ * —— 这正是当天真机反馈的原话。而 `instanceId` 帮不上忙:它是**同一台桌面**的稳定
17
+ * 身份(多入口归一靠它),跨重启必须不变,所以不能用它判断新旧代。
18
+ *
19
+ * 判据只能是 `pid` + `startedAt` 这一对。这里钉住三件事:
20
+ * 1. 两个字段都得在(客户端靠它们证明换代);
21
+ * 2. startedAt 是可解析的 RFC3339(客户端做字符串比较,格式漂了就会误判);
22
+ * 3. pid 就是本进程(不是某个常量、也不是状态文件里的旧值)。
23
+ */
24
+ func TestHealthReportsProcessIdentity(t *testing.T) {
25
+ svc := newTestService(t, fakeCore(t, goodContract))
26
+ srv := httptest.NewServer(svc.Handler())
27
+ defer srv.Close()
28
+
29
+ read := func() map[string]any {
30
+ t.Helper()
31
+ req, err := http.NewRequest(http.MethodGet, srv.URL+"/desk/health", nil)
32
+ if err != nil {
33
+ t.Fatal(err)
34
+ }
35
+ req.Header.Set("X-Magic-Token", "tok")
36
+ res, err := http.DefaultClient.Do(req)
37
+ if err != nil {
38
+ t.Fatal(err)
39
+ }
40
+ defer res.Body.Close()
41
+ if res.StatusCode != http.StatusOK {
42
+ t.Fatalf("期望 200,得到 %d", res.StatusCode)
43
+ }
44
+ var got map[string]any
45
+ if err := json.NewDecoder(res.Body).Decode(&got); err != nil {
46
+ t.Fatal(err)
47
+ }
48
+ return got
49
+ }
50
+
51
+ got := read()
52
+
53
+ pid, ok := got["pid"].(float64) // JSON 数字统一解成 float64
54
+ if !ok {
55
+ t.Fatalf("health 必须带 pid(客户端靠它证明换代),实际没有或类型不对:%#v", got["pid"])
56
+ }
57
+ if int(pid) != os.Getpid() {
58
+ t.Fatalf("pid 必须是本进程 %d,得到 %v", os.Getpid(), pid)
59
+ }
60
+
61
+ raw, ok := got["startedAt"].(string)
62
+ if !ok || raw == "" {
63
+ t.Fatalf("health 必须带 startedAt(RFC3339),实际 %#v", got["startedAt"])
64
+ }
65
+ ts, err := time.Parse(time.RFC3339, raw)
66
+ if err != nil {
67
+ t.Fatalf("startedAt 必须是可解析的 RFC3339(客户端做字符串比较),得到 %q:%v", raw, err)
68
+ }
69
+ /* 启动时刻不该在将来,也不该早得离谱 —— 后者通常意味着拿了包级的零值/编译常量。 */
70
+ if time.Since(ts) < 0 {
71
+ t.Fatalf("startedAt 落在将来:%v", ts)
72
+ }
73
+ if time.Since(ts) > time.Hour {
74
+ t.Fatalf("startedAt 早得不合理(%v 之前):像是没在 New() 里赋值", ts)
75
+ }
76
+
77
+ /* 同一个进程连着问两次,pid 与 startedAt 都必须一模一样 ——
78
+ 否则客户端会把「还没重启」误判成「换代了」。 */
79
+ again := read()
80
+ if again["pid"] != got["pid"] || again["startedAt"] != got["startedAt"] {
81
+ t.Fatalf("同一进程两次应答的身份必须一致:%v/%v vs %v/%v",
82
+ got["pid"], got["startedAt"], again["pid"], again["startedAt"])
83
+ }
84
+ }
@@ -0,0 +1,68 @@
1
+ package client
2
+
3
+ // hostenv.go - 给 core 子进程的准备环境。
4
+ //
5
+ // 为什么在客户端做而不是 core 里做:core 是**被客户端 exec 起来的**,它继承什么
6
+ // 完全由这里决定。宿主 IDE(WorkBuddy)的终端会给客户端塞三样东西:
7
+ //
8
+ // NODE_OPTIONS=--require=".../app.asar.unpacked/.../node-language-shim.cjs"
9
+ // ELECTRON_RUN_AS_NODE=1
10
+ // PATH=<...>/shim/brokered-bin:<...> ← 假 cat/cp/dd,require 时去连宿主 broker
11
+ //
12
+ // 后果(2026-10-26 真机实测):引擎的 node 子进程加载宿主 shim → 连不上 broker →
13
+ // `connect failed: Connection refused` → 每条任务 failed,错误看着像引擎坏了。
14
+ // 在 core 一启动就把这三样摘干净,比让每个引擎各自防御可靠。
15
+ //
16
+ // 判据只认 `.asar` / `workbuddy` / `brokered-bin` 这几个特征:用户自己设的
17
+ // NODE_OPTIONS(--max-old-space-size 之类)与正常 PATH 段原样保留。
18
+
19
+ import (
20
+ "os"
21
+ "strings"
22
+ )
23
+
24
+ // hostShimValue 判断一个值是否指向宿主 Electron 应用的垫片。
25
+ func hostShimValue(v string) bool {
26
+ l := strings.ToLower(v)
27
+ return strings.Contains(l, ".asar") || strings.Contains(l, "workbuddy") || strings.Contains(l, "brokered-bin")
28
+ }
29
+
30
+ // cleanHostEnv 返回一份可以交给 core 的环境:摘掉宿主垫片变量与假命令目录。
31
+ func cleanHostEnv(env []string) []string {
32
+ out := make([]string, 0, len(env))
33
+ for _, kv := range env {
34
+ name, val, ok := strings.Cut(kv, "=")
35
+ if !ok {
36
+ out = append(out, kv)
37
+ continue
38
+ }
39
+ switch name {
40
+ case "ELECTRON_RUN_AS_NODE":
41
+ continue
42
+ case "NODE_OPTIONS":
43
+ if hostShimValue(val) {
44
+ continue
45
+ }
46
+ case "PATH":
47
+ if cleaned := stripHostShimPath(val); cleaned != val {
48
+ out = append(out, "PATH="+cleaned)
49
+ continue
50
+ }
51
+ }
52
+ out = append(out, kv)
53
+ }
54
+ return out
55
+ }
56
+
57
+ // stripHostShimPath 从 PATH 值里摘掉宿主垫片目录,其余段与顺序不动。
58
+ func stripHostShimPath(val string) string {
59
+ parts := strings.Split(val, string(os.PathListSeparator))
60
+ kept := parts[:0]
61
+ for _, p := range parts {
62
+ if p == "" || hostShimValue(p) {
63
+ continue
64
+ }
65
+ kept = append(kept, p)
66
+ }
67
+ return strings.Join(kept, string(os.PathListSeparator))
68
+ }
@@ -0,0 +1,37 @@
1
+ package client
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
7
+
8
+ // 2026-10-26 真机:宿主 IDE 的终端把这三个变量塞进客户端,引擎子进程全崩在 require。
9
+ func TestCleanHostEnvDropsShimVars(t *testing.T) {
10
+ in := []string{
11
+ `NODE_OPTIONS=--require="/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/vendor/shim/node-language-shim.cjs"`,
12
+ "ELECTRON_RUN_AS_NODE=1",
13
+ "PATH=/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/vendor/shim/brokered-bin:/opt/homebrew/bin:/usr/bin",
14
+ "HOME=/Users/x",
15
+ }
16
+ out := cleanHostEnv(in)
17
+ joined := strings.Join(out, "\n")
18
+ for _, bad := range []string{"ELECTRON_RUN_AS_NODE", ".asar", "brokered-bin"} {
19
+ if strings.Contains(joined, bad) {
20
+ t.Fatalf("还留着 %s:\n%s", bad, joined)
21
+ }
22
+ }
23
+ if !strings.Contains(joined, "PATH=/opt/homebrew/bin:/usr/bin") {
24
+ t.Fatalf("PATH 被误改:\n%s", joined)
25
+ }
26
+ if !strings.Contains(joined, "HOME=/Users/x") {
27
+ t.Fatal("无关变量被删了")
28
+ }
29
+ }
30
+
31
+ // 用户自己设的 NODE_OPTIONS 要留着。
32
+ func TestCleanHostEnvKeepsUserNodeOptions(t *testing.T) {
33
+ out := cleanHostEnv([]string{"NODE_OPTIONS=--max-old-space-size=4096"})
34
+ if len(out) != 1 || out[0] != "NODE_OPTIONS=--max-old-space-size=4096" {
35
+ t.Fatalf("误删:%v", out)
36
+ }
37
+ }