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.
- package/CLIENT-CONTRACT.md +467 -41
- package/README.md +15 -14
- package/client-ui/pc/app.css +74 -0
- package/client-ui/pc/js/04-task-list.js +5 -1
- package/client-ui/pc/js/07-chat-stream-render.js +3 -3
- package/client-ui/pc/js/90-glue.js +40 -2
- package/client-ui/pc/js/92-composer.js +19 -4
- package/client-ui/pc/js/93-rail.js +16 -5
- package/client-ui/shared/conv/02-media-kinds.js +2 -2
- package/client-ui/shared/conv/03-chat-message.js +247 -23
- package/client-ui/shared/conv/06-ask-answer.js +75 -1
- package/client-ui/shared/conv/09-stream-apply.js +72 -7
- package/client-ui/shared/conv/19-attachments.js +157 -6
- package/client-ui/shared/conv/27-conv-ledger.js +5 -2
- package/client-ui/shared/conv/29-msg-variants.js +217 -0
- package/client-ui/shared/conv/conv.css +329 -174
- package/client-ui/shared/conv/dom-map.md +1 -0
- package/client-ui/shared/conversation.css +13 -2
- package/client-ui/shared/conversation.js +373 -50
- package/client-ui/shared/transport-http.js +14 -0
- package/client-ui-mobile/apps.css +16 -0
- package/client-ui-mobile/apps.js +406 -0
- package/client-ui-mobile/index.html +5554 -639
- package/client-ui-mobile/reqs.css +110 -1
- package/client-ui-mobile/reqs.js +533 -74
- package/client-ui-mobile/reqshot.js +505 -0
- package/client-ui-mobile/scripts.css +67 -0
- package/client-ui-mobile/scripts.js +599 -0
- package/client-ui-mobile/vendor/cose-base.js +3214 -0
- package/client-ui-mobile/vendor/cytoscape-fcose.js +1549 -0
- package/client-ui-mobile/vendor/cytoscape.min.js +32 -0
- package/client-ui-mobile/vendor/layout-base.js +5230 -0
- package/cmd/magic-client/browsectl.go +199 -0
- package/cmd/magic-client/clientctl.go +69 -10
- package/cmd/magic-client/main.go +210 -32
- package/internal/agent/ask.go +45 -0
- package/internal/agent/ask_test.go +39 -0
- package/internal/agent/attachments_test.go +6 -2
- package/internal/agent/capability.go +7 -1
- package/internal/agent/claude.go +20 -15
- package/internal/agent/codebuddy.go +125 -34
- package/internal/agent/codebuddy_account_test.go +54 -0
- package/internal/agent/codebuddy_stream_test.go +103 -0
- package/internal/agent/credits.go +713 -0
- package/internal/agent/credits_minimax_test.go +97 -0
- package/internal/agent/credits_test.go +211 -0
- package/internal/agent/custom_models.go +155 -0
- package/internal/agent/custom_models_test.go +125 -0
- package/internal/agent/dsh.go +123 -49
- package/internal/agent/dsh_caps_cache_test.go +184 -0
- package/internal/agent/dsh_resume.go +157 -7
- package/internal/agent/dsh_test.go +108 -6
- package/internal/agent/engine.go +37 -1
- package/internal/agent/engine_base.go +21 -17
- package/internal/agent/engine_base_test.go +2 -2
- package/internal/agent/engine_test.go +47 -86
- package/internal/agent/env.go +173 -1
- package/internal/agent/env_test.go +114 -0
- package/internal/agent/fallback.go +313 -0
- package/internal/agent/fallback_test.go +340 -0
- package/internal/agent/launch.go +43 -1
- package/internal/agent/limitnotice.go +207 -1
- package/internal/agent/limitnotice_test.go +140 -0
- package/internal/agent/llmhttp.go +46 -4
- package/internal/agent/llmhttp_test.go +157 -0
- package/internal/agent/models.go +53 -1
- package/internal/agent/models_test.go +20 -5
- package/internal/agent/pidalive_windows_test.go +40 -0
- package/internal/agent/runcmd.go +26 -0
- package/internal/agent/runcmd_windows.go +36 -5
- package/internal/agent/stream.go +166 -6
- package/internal/agent/stream_test.go +25 -38
- package/internal/agent/stream_timeline_test.go +2 -37
- package/internal/agent/streamjson.go +119 -7
- package/internal/agent/tools_suffix_test.go +6 -3
- package/internal/agent/trae.go +50 -243
- package/internal/agent/trae_exec.go +433 -0
- package/internal/agent/trae_exec_test.go +181 -0
- package/internal/agent/wraplaunch_test.go +113 -0
- package/internal/cli/ask.go +168 -37
- package/internal/cli/ask_test.go +1 -1
- package/internal/cli/checkin.go +77 -0
- package/internal/cli/clientcmd.go +3 -2
- package/internal/cli/control.go +46 -5
- package/internal/cli/control_test.go +65 -0
- package/internal/cli/engines_test.go +103 -0
- package/internal/cli/events.go +15 -0
- package/internal/cli/events_test.go +50 -0
- package/internal/cli/judge.go +104 -0
- package/internal/cli/quotas.go +125 -0
- package/internal/cli/requirements.go +51 -0
- package/internal/cli/root.go +157 -11
- package/internal/cli/session.go +75 -3
- package/internal/cli/session_test.go +82 -3
- package/internal/cli/sessionlog.go +6 -0
- package/internal/cli/sessionlog_test.go +22 -2
- package/internal/cli/suggest.go +7 -5
- package/internal/cli/team.go +37 -9
- package/internal/cli/team_test.go +39 -0
- package/internal/client/ab.go +372 -0
- package/internal/client/ab_test.go +287 -0
- package/internal/client/apk.go +8 -0
- package/internal/client/apk_test.go +59 -0
- package/internal/client/apps.go +1078 -0
- package/internal/client/apps_test.go +516 -0
- package/internal/client/browse.go +84 -36
- package/internal/client/browse_test.go +107 -9
- package/internal/client/browseinput.go +481 -0
- package/internal/client/browseinput_test.go +216 -0
- package/internal/client/corecmd.go +23 -0
- package/internal/client/corecmd_other.go +8 -0
- package/internal/client/corecmd_windows.go +24 -0
- package/internal/client/coreexe.go +196 -0
- package/internal/client/coreexe_test.go +161 -0
- package/internal/client/coreupgrade.go +866 -0
- package/internal/client/coreupgrade_test.go +561 -0
- package/internal/client/daemon.go +297 -33
- package/internal/client/daemon_test.go +33 -0
- package/internal/client/engines_checkin_test.go +57 -0
- package/internal/client/fallback_flags_test.go +64 -0
- package/internal/client/feishu.go +1259 -0
- package/internal/client/feishu_test.go +649 -0
- package/internal/client/health_identity_test.go +84 -0
- package/internal/client/hostenv.go +68 -0
- package/internal/client/hostenv_test.go +37 -0
- package/internal/client/jobs.go +518 -24
- package/internal/client/jobs_auto.go +571 -0
- package/internal/client/jobs_auto_test.go +431 -0
- package/internal/client/jobs_test.go +299 -2
- package/internal/client/knowledge.go +854 -0
- package/internal/client/knowledge_test.go +510 -0
- package/internal/client/lan.go +3 -3
- package/internal/client/plugins.go +5 -1
- package/internal/client/prefs.go +67 -0
- package/internal/client/procstart_linux.go +59 -0
- package/internal/client/publicurl.go +82 -0
- package/internal/client/quotas_merge_test.go +107 -0
- package/internal/client/requirements.go +207 -2
- package/internal/client/requirements_shots_test.go +200 -0
- package/internal/client/requirements_test.go +103 -0
- package/internal/client/restart.go +288 -0
- package/internal/client/restart_handoff_test.go +230 -0
- package/internal/client/restart_test.go +290 -0
- package/internal/client/scripts.go +1047 -0
- package/internal/client/scripts_decode_implausible.go +38 -0
- package/internal/client/scripts_decode_other.go +31 -0
- package/internal/client/scripts_decode_test.go +130 -0
- package/internal/client/scripts_decode_windows.go +182 -0
- package/internal/client/scripts_test.go +739 -0
- package/internal/client/service.go +717 -97
- package/internal/client/service_test.go +217 -1
- package/internal/client/sessions_cache.go +148 -0
- package/internal/client/sessions_cache_test.go +163 -0
- package/internal/client/sessions_fold.go +165 -0
- package/internal/client/sessions_fold_test.go +204 -0
- package/internal/client/singleinstance.go +470 -0
- package/internal/client/singleinstance_test.go +607 -0
- package/internal/client/suggest.go +105 -2
- package/internal/client/suggest_test.go +86 -0
- package/internal/client/team_flags.go +25 -11
- package/internal/client/team_flags_test.go +39 -0
- package/internal/client/testmain_test.go +88 -0
- package/internal/client/workspace_default_test.go +158 -0
- package/internal/client/workspacelockenv_test.go +54 -0
- package/internal/config/config.go +48 -0
- package/internal/judge/judge.go +315 -0
- package/internal/requirements/bootstrap.go +16 -0
- package/internal/requirements/consolidate.go +100 -9
- package/internal/requirements/doc.go +154 -3
- package/internal/requirements/graph.go +138 -0
- package/internal/requirements/memory.go +7 -0
- package/internal/requirements/modules.go +312 -0
- package/internal/requirements/modules_test.go +373 -0
- package/internal/requirements/relations.go +155 -0
- package/internal/requirements/relations_test.go +231 -0
- package/internal/requirements/requirements.go +10 -0
- package/internal/requirements/shots.go +418 -0
- package/internal/requirements/shots_test.go +387 -0
- package/internal/session/multiclient_test.go +368 -0
- package/internal/session/session.go +175 -11
- package/internal/session/session_test.go +7 -0
- package/internal/session/status.go +21 -0
- package/internal/session/status_test.go +28 -0
- package/internal/session/title.go +16 -0
- package/internal/session/title_test.go +26 -0
- package/internal/session/workspace_lock.go +265 -28
- package/internal/suggest/suggest.go +23 -1
- package/internal/team/event.go +3 -1
- package/internal/team/execute.go +21 -6
- package/internal/team/fallback_route_test.go +80 -0
- package/internal/team/planner.go +15 -3
- package/internal/team/route.go +89 -17
- package/internal/team/team.go +18 -4
- package/npm/apk/magic-agent-mobile-release.apk +0 -0
- package/npm/apk/version.json +6 -3
- package/npm/dist/darwin-arm64/magic-agent +0 -0
- package/npm/dist/darwin-arm64/magic-client +0 -0
- package/npm/dist/darwin-x64/magic-agent +0 -0
- package/npm/dist/darwin-x64/magic-client +0 -0
- package/npm/dist/linux-arm64/magic-agent +0 -0
- package/npm/dist/linux-arm64/magic-client +0 -0
- package/npm/dist/linux-x64/magic-agent +0 -0
- package/npm/dist/linux-x64/magic-client +0 -0
- package/npm/dist/win32-x64/magic-agent.exe +0 -0
- package/npm/dist/win32-x64/magic-client.exe +0 -0
- package/npm/install.js +11 -0
- 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
|
+
}
|