magic-agent 2.4.2 → 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.
- package/CLIENT-CONTRACT.md +290 -30
- package/README.md +15 -14
- package/client-ui/pc/app.css +32 -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 +37 -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 +241 -23
- package/client-ui/shared/conv/06-ask-answer.js +75 -1
- package/client-ui/shared/conv/09-stream-apply.js +63 -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 +81 -0
- package/client-ui/shared/conv/conv.css +79 -19
- package/client-ui/shared/conv/dom-map.md +1 -0
- package/client-ui/shared/conversation.css +13 -2
- package/client-ui/shared/conversation.js +336 -50
- package/client-ui/shared/transport-http.js +14 -0
- package/client-ui-mobile/index.html +2932 -333
- package/client-ui-mobile/reqs.css +102 -0
- package/client-ui-mobile/reqs.js +485 -70
- package/client-ui-mobile/reqshot.js +505 -0
- package/client-ui-mobile/scripts.css +21 -0
- package/client-ui-mobile/scripts.js +539 -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 +33 -9
- package/cmd/magic-client/main.go +57 -26
- 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_resume.go +13 -2
- 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/fallback.go +313 -0
- package/internal/agent/fallback_test.go +340 -0
- package/internal/agent/launch.go +43 -1
- 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 +136 -6
- package/internal/agent/stream_test.go +25 -38
- package/internal/agent/stream_timeline_test.go +2 -37
- package/internal/agent/streamjson.go +108 -6
- 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 +160 -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/events.go +4 -0
- package/internal/cli/quotas.go +125 -0
- package/internal/cli/requirements.go +51 -0
- package/internal/cli/root.go +122 -5
- package/internal/cli/session.go +7 -10
- 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/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/daemon.go +90 -17
- 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/jobs.go +294 -21
- 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 +193 -7
- package/internal/client/lan_test.go +110 -0
- package/internal/client/quotas_merge_test.go +107 -0
- package/internal/client/requirements.go +198 -1
- package/internal/client/requirements_shots_test.go +200 -0
- package/internal/client/restart.go +134 -0
- package/internal/client/restart_test.go +151 -0
- package/internal/client/scripts.go +1047 -0
- package/internal/client/scripts_decode_other.go +27 -0
- package/internal/client/scripts_decode_test.go +125 -0
- package/internal/client/scripts_decode_windows.go +206 -0
- package/internal/client/scripts_test.go +726 -0
- package/internal/client/service.go +322 -18
- package/internal/client/service_test.go +51 -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/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/session.go +92 -0
- package/internal/session/sessionlog.go +23 -0
- package/internal/session/sessionlog_read_test.go +39 -0
- 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 +1 -5
- 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/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
|
+
}
|