magic-agent 1.0.3 → 1.0.49
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 +254 -8
- package/README.md +48 -5
- package/client-ui/index.html +32 -0
- package/client-ui/pc/app.css +89 -1
- package/client-ui/pc/index.html +18 -8
- package/client-ui/pc/js/00-base.js +8 -30
- package/client-ui/pc/js/05-task-list-render.js +4 -1
- package/client-ui/pc/js/07-chat-stream-render.js +2 -85
- package/client-ui/pc/js/10-markdown.js +3 -171
- package/client-ui/pc/js/18-askbox.js +2 -176
- package/client-ui/pc/js/90-glue.js +1 -1
- package/client-ui/pc/js/91-workbench-stubs.js +4 -10
- package/client-ui/pc/js/93-rail.js +10 -3
- package/client-ui/shared/conv/01-dom.js +52 -0
- package/client-ui/shared/conv/02-media-kinds.js +34 -0
- package/client-ui/shared/conv/03-chat-message.js +1561 -0
- package/client-ui/shared/conv/06-ask-answer.js +377 -0
- package/client-ui/shared/conv/07-stream-delegation.js +158 -0
- package/client-ui/{pc/js → shared/conv}/09-stream-apply.js +121 -6
- package/client-ui/shared/conv/10-markdown-render.js +252 -0
- package/client-ui/shared/conv/18-askbox-markup.js +185 -0
- package/client-ui/{pc/js → shared/conv}/19-attachments.js +365 -21
- package/client-ui/shared/conv/27-conv-ledger.js +381 -0
- package/client-ui/{pc/js → shared/conv}/29-msg-variants.js +112 -3
- package/client-ui/shared/conv/README.md +257 -0
- package/client-ui/shared/conv/conv.css +1638 -0
- package/client-ui/shared/conv/dom-map.md +170 -0
- package/client-ui/shared/conv/manifest.json +44 -0
- package/client-ui/shared/conversation.css +368 -190
- package/client-ui/shared/conversation.js +1722 -788
- package/client-ui/shared/host-glue.js +143 -0
- package/client-ui/shared/transport-http.js +130 -6
- package/client-ui-mobile/index.html +2417 -103
- package/cmd/magic-client/clientctl.go +273 -0
- package/cmd/magic-client/main.go +71 -5
- package/go.mod +6 -3
- package/go.sum +4 -1
- package/internal/agent/arkclaw.go +3 -2
- package/internal/agent/arkclaw_stream.go +126 -8
- package/internal/agent/arkclaw_stream_test.go +97 -15
- package/internal/agent/arkclaw_test.go +3 -3
- package/internal/agent/attachments.go +18 -5
- package/internal/agent/attachments_test.go +7 -1
- package/internal/agent/capability_test.go +11 -1
- package/internal/agent/claude.go +96 -158
- package/internal/agent/codebuddy.go +108 -133
- package/internal/agent/codebuddy_gateway.go +8 -4
- package/internal/agent/codebuddy_gateway_stream.go +82 -2
- package/internal/agent/codebuddy_gateway_test.go +236 -1
- package/internal/agent/codebuddy_stream_test.go +262 -0
- package/internal/agent/codex.go +50 -9
- package/internal/agent/codex_stream_test.go +30 -0
- package/internal/agent/codex_test.go +24 -0
- package/internal/agent/custom_models.go +109 -3
- package/internal/agent/custom_models_test.go +74 -0
- package/internal/agent/dsh.go +464 -130
- package/internal/agent/dsh_resume.go +258 -0
- package/internal/agent/dsh_sdk.go +68 -17
- package/internal/agent/dsh_sdk_test.go +102 -2
- package/internal/agent/dsh_test.go +409 -36
- package/internal/agent/engine.go +26 -6
- package/internal/agent/engine_base.go +46 -2
- package/internal/agent/engine_base_test.go +29 -0
- package/internal/agent/engine_test.go +101 -0
- package/internal/agent/env.go +69 -1
- package/internal/agent/itemtracker.go +271 -0
- package/internal/agent/launch.go +192 -0
- package/internal/agent/launch_test.go +206 -0
- package/internal/agent/launch_unix.go +15 -0
- package/internal/agent/launch_windows.go +24 -0
- package/internal/agent/limitnotice.go +103 -0
- package/internal/agent/limitnotice_test.go +211 -0
- package/internal/agent/llmengine.go +2 -2
- package/internal/agent/models.go +3 -2
- package/internal/agent/openclaw.go +4 -3
- package/internal/agent/openclaw_acp.go +7 -0
- package/internal/agent/openclaw_test.go +2 -2
- package/internal/agent/permission.go +100 -11
- package/internal/agent/permission_test.go +100 -36
- package/internal/agent/runcmd.go +13 -3
- package/internal/agent/runner.go +7 -1
- package/internal/agent/runner_test.go +1 -1
- package/internal/agent/stream.go +777 -43
- package/internal/agent/stream_test.go +3 -2
- package/internal/agent/stream_timeline_test.go +905 -0
- package/internal/agent/streamjson.go +36 -2
- package/internal/agent/toolkind.go +20 -1
- package/internal/agent/toolkind_test.go +7 -1
- package/internal/agent/tools_suffix_test.go +10 -3
- package/internal/agent/trae.go +5 -3
- package/internal/agent/workspace_test.go +52 -0
- package/internal/cli/ask.go +260 -33
- package/internal/cli/ask_test.go +15 -11
- package/internal/cli/attach_test.go +32 -20
- package/internal/cli/clientcmd.go +156 -0
- package/internal/cli/events.go +53 -8
- package/internal/cli/events_test.go +230 -0
- package/internal/cli/keepalive_test.go +101 -6
- package/internal/cli/permission_test.go +5 -1
- package/internal/cli/root.go +174 -32
- package/internal/cli/session.go +175 -28
- package/internal/cli/session_test.go +144 -5
- package/internal/cli/sessionlog.go +385 -38
- package/internal/cli/sessionlog_test.go +708 -0
- package/internal/cli/sessionlog_user_test.go +142 -1
- package/internal/cli/sessiontitle.go +141 -0
- package/internal/client/browse.go +456 -0
- package/internal/client/browse_test.go +199 -0
- package/internal/client/cors_test.go +35 -0
- package/internal/client/daemon.go +525 -0
- package/internal/client/daemon_test.go +212 -0
- package/internal/client/detach_unix.go +19 -0
- package/internal/client/detach_windows.go +27 -0
- package/internal/client/pair.go +3 -3
- package/internal/client/plugins.go +376 -0
- package/internal/client/plugins_test.go +223 -0
- package/internal/client/proxy.go +374 -0
- package/internal/client/proxy_test.go +212 -0
- package/internal/client/service.go +1464 -48
- package/internal/client/service_test.go +649 -206
- package/internal/client/session_events_test.go +130 -0
- package/internal/client/wsproxy.go +113 -0
- package/internal/client/wsproxy_test.go +102 -0
- package/internal/session/delete.go +151 -0
- package/internal/session/delete_test.go +103 -0
- package/internal/session/session.go +155 -20
- package/internal/session/session_append.go +79 -21
- package/internal/session/session_test.go +124 -10
- package/internal/session/sessionlog.go +572 -6
- package/internal/session/sessionlog_read_test.go +410 -0
- package/internal/session/title.go +486 -0
- package/internal/session/title_test.go +298 -0
- package/npm/bin/magic-agent.js +101 -10
- package/npm/bin/magic-client.js +118 -0
- 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 +36 -13
- package/npm/lib/platform.js +27 -5
- package/package.json +8 -3
- package/client-ui/pc/js/03-chat-message.js +0 -880
- package/client-ui/pc/js/06-ask-answer.js +0 -204
- /package/client-ui/{pc/js → shared/conv}/08-ask-event-map.js +0 -0
- /package/client-ui/{pc → shared}/vendor/highlight.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/marked.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/purify.min.js +0 -0
|
@@ -103,7 +103,7 @@ func TestClaudePermissionModeMapping(t *testing.T) {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
// TestPermissionSupportOf
|
|
106
|
+
// TestPermissionSupportOf 能力表:claude / codebuddy / codebuddy-ai 走 flag,dsh 走环境变量。
|
|
107
107
|
func TestPermissionSupportOf(t *testing.T) {
|
|
108
108
|
for _, e := range []string{"claude", "codebuddy", "codebuddy-ai"} {
|
|
109
109
|
if !PermissionSupported(e) {
|
|
@@ -113,8 +113,15 @@ func TestPermissionSupportOf(t *testing.T) {
|
|
|
113
113
|
t.Errorf("PermissionSupportOf(%q) = %q", e, got)
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
+
// dsh 没有逐调用权限 flag,档位走子进程环境变量(shipped 的 sdk profile 直接读它)。
|
|
117
|
+
if !PermissionSupported("dsh") {
|
|
118
|
+
t.Error("dsh 应支持四档模型(经 DSH_PERMISSION_MODE)")
|
|
119
|
+
}
|
|
120
|
+
if got := PermissionSupportOf("dsh"); got != "env:DSH_PERMISSION_MODE" {
|
|
121
|
+
t.Errorf("PermissionSupportOf(\"dsh\") = %q, want env:DSH_PERMISSION_MODE", got)
|
|
122
|
+
}
|
|
116
123
|
// 未接线的引擎必须报 none —— CLI 层据此 exit 2,而不是静默忽略。
|
|
117
|
-
for _, e := range []string{"trae", "llm", "codex", "openclaw", "
|
|
124
|
+
for _, e := range []string{"trae", "llm", "codex", "openclaw", "arkclaw"} {
|
|
118
125
|
if PermissionSupported(e) {
|
|
119
126
|
t.Errorf("%s 不应报告支持四档模型", e)
|
|
120
127
|
}
|
|
@@ -124,6 +131,40 @@ func TestPermissionSupportOf(t *testing.T) {
|
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
|
|
134
|
+
// TestDshPermissionMode 四档 → dsh SandboxMode 的映射,以及**空档位不注入**这条红线。
|
|
135
|
+
//
|
|
136
|
+
// 空档位那条是重点:DefaultPermissionTier 是 full,若 dshPermissionMode 在空串上兜默认值,
|
|
137
|
+
// `magic-agent -e dsh`(未显式传档)就会静默把 dsh 的沙箱整个关掉 —— 这正是本仓库最忌讳的
|
|
138
|
+
// 「静默的安全降级」,故用测试钉住。
|
|
139
|
+
func TestDshPermissionMode(t *testing.T) {
|
|
140
|
+
cases := []struct {
|
|
141
|
+
tier PermissionTier
|
|
142
|
+
mode string
|
|
143
|
+
}{
|
|
144
|
+
{"", ""}, // 空档 = 不注入(用 dsh 自己的默认,保持既有行为)
|
|
145
|
+
{PermissionManual, "read-only"},
|
|
146
|
+
{PermissionAcceptEdits, "workspace-write"},
|
|
147
|
+
{PermissionAuto, "workspace-write"},
|
|
148
|
+
{PermissionFull, "danger-full-access"},
|
|
149
|
+
}
|
|
150
|
+
for _, c := range cases {
|
|
151
|
+
if got := dshPermissionMode(c.tier); got != c.mode {
|
|
152
|
+
t.Errorf("dshPermissionMode(%q) = %q, want %q", c.tier, got, c.mode)
|
|
153
|
+
}
|
|
154
|
+
env := dshPermissionEnv(c.tier)
|
|
155
|
+
if c.mode == "" {
|
|
156
|
+
if env != nil {
|
|
157
|
+
t.Errorf("dshPermissionEnv(%q) 应返回 nil(不注入),得到 %v", c.tier, env)
|
|
158
|
+
}
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
want := []string{"DSH_PERMISSION_MODE=" + c.mode}
|
|
162
|
+
if len(env) != 1 || env[0] != want[0] {
|
|
163
|
+
t.Errorf("dshPermissionEnv(%q) = %v, want %v", c.tier, env, want)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
127
168
|
// ── settings 载荷 ─────────────────────────────────────────────
|
|
128
169
|
|
|
129
170
|
// decodeSettings 把 --settings 的内联 JSON 解成嵌套 map 便于断言。
|
|
@@ -154,6 +195,13 @@ func dig(m map[string]any, path ...string) any {
|
|
|
154
195
|
// 关键点:autoAllowBashIfSandboxed 是第 1/2 档与第 3 档的分水岭。
|
|
155
196
|
// 该键在 claude 侧的默认值是 true,所以第 1/2 档必须显式写 false ——
|
|
156
197
|
// 否则沙箱内的 Bash 会被自动放行,第 2 档就退化成了「编辑与命令都不问」。
|
|
198
|
+
//
|
|
199
|
+
// ⚠️ 2026-10-01 反转:第 4 档(PermissionFull / bypassPermissions)下,
|
|
200
|
+
// sandboxSettings **返回 nil** 而不是 `{"enabled": false}`。原因是 claude CLI
|
|
201
|
+
// 在 tools=default + bypassPermissions + `--settings {"sandbox":{"enabled":false}}`
|
|
202
|
+
// 组合下报「Invalid JSON provided to --settings」、exit 0 零输出(实测)。
|
|
203
|
+
// bypassPermissions 与 sandbox 互斥:开了也是空跑、显式写 enabled=false 反而触发 bug。
|
|
204
|
+
// 让外层(agentSettingsPayload)整段不下发即可。
|
|
157
205
|
func TestSandboxSettingsPerTier(t *testing.T) {
|
|
158
206
|
cases := []struct {
|
|
159
207
|
tier PermissionTier
|
|
@@ -164,19 +212,16 @@ func TestSandboxSettingsPerTier(t *testing.T) {
|
|
|
164
212
|
{PermissionManual, true, false, true},
|
|
165
213
|
{PermissionAcceptEdits, true, false, true},
|
|
166
214
|
{PermissionAuto, true, true, true},
|
|
167
|
-
{PermissionFull, false, false, false},
|
|
168
215
|
}
|
|
169
216
|
for _, c := range cases {
|
|
170
217
|
sb := sandboxSettings(c.tier, PermissionOptions{}, "claude")
|
|
218
|
+
if sb == nil {
|
|
219
|
+
t.Errorf("%s: sandboxSettings 不应返回 nil", c.tier)
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
171
222
|
if got := sb["enabled"]; got != c.enabled {
|
|
172
223
|
t.Errorf("%s: sandbox.enabled = %v, want %v", c.tier, got, c.enabled)
|
|
173
224
|
}
|
|
174
|
-
if c.tier == PermissionFull {
|
|
175
|
-
if _, has := sb["autoAllowBashIfSandboxed"]; has {
|
|
176
|
-
t.Errorf("full 档不该带 autoAllowBashIfSandboxed")
|
|
177
|
-
}
|
|
178
|
-
continue
|
|
179
|
-
}
|
|
180
225
|
if got := sb["autoAllowBashIfSandboxed"]; got != c.autoAllow {
|
|
181
226
|
t.Errorf("%s: sandbox.autoAllowBashIfSandboxed = %v, want %v(这是第1/2档与第3档的分水岭)",
|
|
182
227
|
c.tier, got, c.autoAllow)
|
|
@@ -199,6 +244,11 @@ func TestSandboxSettingsPerTier(t *testing.T) {
|
|
|
199
244
|
if got := sb["enabled"]; got != true {
|
|
200
245
|
t.Errorf("codebuddy auto 档 sandbox.enabled = %v, want true", got)
|
|
201
246
|
}
|
|
247
|
+
|
|
248
|
+
// 第 4 档:返回 nil,整段 sandbox 不下发。
|
|
249
|
+
if got := sandboxSettings(PermissionFull, PermissionOptions{}, "claude"); got != nil {
|
|
250
|
+
t.Errorf("full 档应返回 nil(实测 2026-10-01:写 enabled=false 触发 claude CLI bug)")
|
|
251
|
+
}
|
|
202
252
|
}
|
|
203
253
|
|
|
204
254
|
// TestSandboxSettingsOptions 可选项(排除命令 / 网络白名单 / 放宽逃逸舱口)。
|
|
@@ -221,9 +271,9 @@ func TestSandboxSettingsOptions(t *testing.T) {
|
|
|
221
271
|
t.Errorf("allowUnsandboxedCommands = %v, want true(显式放宽时)", got)
|
|
222
272
|
}
|
|
223
273
|
|
|
224
|
-
// 第 4
|
|
225
|
-
if sbFull := sandboxSettings(PermissionFull, opts, "claude");
|
|
226
|
-
t.Errorf("full 档 sandbox
|
|
274
|
+
// 第 4 档:返回 nil,所有可选项一律不生效。
|
|
275
|
+
if sbFull := sandboxSettings(PermissionFull, opts, "claude"); sbFull != nil {
|
|
276
|
+
t.Errorf("full 档 sandbox 应返回 nil(整段不下发),得到 %v", sbFull)
|
|
227
277
|
}
|
|
228
278
|
}
|
|
229
279
|
|
|
@@ -314,17 +364,17 @@ func TestAgentSettingsPayload(t *testing.T) {
|
|
|
314
364
|
t.Error("permissions.ask 丢失")
|
|
315
365
|
}
|
|
316
366
|
|
|
317
|
-
// ④ 第 4
|
|
367
|
+
// ④ 第 4 档:**不下发 sandbox 整段**(实测 2026-10-01:claude CLI 在
|
|
368
|
+
// tools=default + bypassPermissions + `--settings {"sandbox":{"enabled":false}}`
|
|
369
|
+
// 组合下报「Invalid JSON provided to --settings」、exit 0 零输出)。
|
|
370
|
+
// bypassPermissions 与 sandbox 互斥:开了也是空跑、显式写 enabled=false
|
|
371
|
+
// 反而触发那个 bug。让外层 sandboxSettings 返 nil、外层整段不下发即可。
|
|
318
372
|
payload, ok = agentSettingsPayload(Request{Tools: ToolsOn, Permission: PermissionFull}, "claude")
|
|
319
|
-
if
|
|
320
|
-
t.
|
|
373
|
+
if ok {
|
|
374
|
+
t.Error("full 档应**不**注入 --settings(沙箱整段不下发)")
|
|
321
375
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
t.Errorf("full 档 sandbox.enabled = %v, want false", got)
|
|
325
|
-
}
|
|
326
|
-
if _, has := s["autoMode"]; has {
|
|
327
|
-
t.Error("full 档不该有 autoMode")
|
|
376
|
+
if payload != "" {
|
|
377
|
+
t.Errorf("full 档 payload 应为空,实际 %q", payload)
|
|
328
378
|
}
|
|
329
379
|
}
|
|
330
380
|
|
|
@@ -374,7 +424,8 @@ func TestClaudeBuildArgsPermissionTiers(t *testing.T) {
|
|
|
374
424
|
{PermissionFull, "bypassPermissions"},
|
|
375
425
|
}
|
|
376
426
|
for _, c := range cases {
|
|
377
|
-
// --tools on
|
|
427
|
+
// --tools on:2026-10-01 必须显式 --tools default(不然 claude CLI 在
|
|
428
|
+
// stream-json 模式下不发 result 行);同时仍带 --permission-mode。
|
|
378
429
|
args := e.buildArgsBase(Request{Tools: ToolsOn, Permission: c.tier})
|
|
379
430
|
got, ok := argValue(args, "--permission-mode")
|
|
380
431
|
if !ok {
|
|
@@ -383,8 +434,8 @@ func TestClaudeBuildArgsPermissionTiers(t *testing.T) {
|
|
|
383
434
|
if got != c.wantMode {
|
|
384
435
|
t.Errorf("%s(--tools on): --permission-mode = %q, want %q", c.tier, got, c.wantMode)
|
|
385
436
|
}
|
|
386
|
-
if
|
|
387
|
-
t.Errorf("%s(--tools on):
|
|
437
|
+
if got, _ := argValue(args, "--tools"); got != "default" {
|
|
438
|
+
t.Errorf("%s(--tools on): --tools = %q, want default(显式开全部内置)", c.tier, got)
|
|
388
439
|
}
|
|
389
440
|
// 改造后不该再出现 --dangerously-skip-permissions(档位统一走 --permission-mode)。
|
|
390
441
|
for _, a := range args {
|
|
@@ -392,9 +443,22 @@ func TestClaudeBuildArgsPermissionTiers(t *testing.T) {
|
|
|
392
443
|
t.Errorf("%s: 仍在传 --dangerously-skip-permissions,应统一走 --permission-mode", c.tier)
|
|
393
444
|
}
|
|
394
445
|
}
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
446
|
+
// 第 4 档**不**带 --settings(实测 2026-10-01:claude CLI 在
|
|
447
|
+
// tools=default + bypassPermissions + `{"sandbox":{"enabled":false}}`
|
|
448
|
+
// 组合下报「Invalid JSON provided to --settings」、exit 0 零输出)。
|
|
449
|
+
// 其他档:沙箱只能经 --settings 进(claude 没有 --sandbox flag)。
|
|
450
|
+
hasSettings := false
|
|
451
|
+
if _, has := argValue(args, "--settings"); has {
|
|
452
|
+
hasSettings = true
|
|
453
|
+
}
|
|
454
|
+
if c.tier == PermissionFull {
|
|
455
|
+
if hasSettings {
|
|
456
|
+
t.Errorf("%s(--tools on): full 档不应带 --settings(实测 2026-10-01 触发 claude CLI bug)", c.tier)
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
if !hasSettings {
|
|
460
|
+
t.Errorf("%s(--tools on): 缺少 --settings(沙箱配置没有 CLI flag,只能从这里进)", c.tier)
|
|
461
|
+
}
|
|
398
462
|
}
|
|
399
463
|
|
|
400
464
|
// --tools 白名单
|
|
@@ -422,22 +486,22 @@ func TestClaudeBuildArgsPermissionTiers(t *testing.T) {
|
|
|
422
486
|
|
|
423
487
|
// TestClaudeBuildArgsDefaultTierUnchanged 默认档的 argv 与改造前的语义一致。
|
|
424
488
|
//
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
//
|
|
489
|
+
// 2026-10-01 修订:默认档(Permission 零值 = PermissionFull / bypassPermissions)
|
|
490
|
+
// **不**带 --settings。原因是 claude CLI 在 tools=default + bypassPermissions +
|
|
491
|
+
// `{"sandbox":{"enabled":false}}` 组合下报「Invalid JSON provided to --settings」、
|
|
492
|
+
// exit 0 零输出(实测)。bypassPermissions 与 sandbox 互斥,写 enabled=false 反而
|
|
493
|
+
// 触发那个 bug。让 sandboxSettings 在 full 档返 nil、外层整段不下发即可。
|
|
429
494
|
func TestClaudeBuildArgsDefaultTierUnchanged(t *testing.T) {
|
|
430
495
|
e := &ClaudeEngine{}
|
|
431
496
|
args := e.buildArgsBase(Request{Tools: ToolsOn}) // Permission 零值
|
|
432
497
|
if got, _ := argValue(args, "--permission-mode"); got != "bypassPermissions" {
|
|
433
498
|
t.Errorf("默认档 --permission-mode = %q, want bypassPermissions", got)
|
|
434
499
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
t.Fatal("默认档应带 --settings(显式关闭沙箱)")
|
|
500
|
+
if got, _ := argValue(args, "--tools"); got != "default" {
|
|
501
|
+
t.Errorf("默认档 --tools = %q, want default(2026-10-01 必须显式 --tools default)", got)
|
|
438
502
|
}
|
|
439
|
-
if
|
|
440
|
-
t.Errorf("
|
|
503
|
+
if _, has := argValue(args, "--settings"); has {
|
|
504
|
+
t.Errorf("默认档(full)不应带 --settings(实测触发 claude CLI bug)")
|
|
441
505
|
}
|
|
442
506
|
}
|
|
443
507
|
|
package/internal/agent/runcmd.go
CHANGED
|
@@ -73,7 +73,10 @@ func runCLIEnv(ctx context.Context, extra []string, bin string, args ...string)
|
|
|
73
73
|
|
|
74
74
|
// runCLIEnvIn = runCLIIn + runCLIEnv(工作目录 + 追加环境变量)。
|
|
75
75
|
func runCLIEnvIn(ctx context.Context, dir string, extra []string, bin string, args ...string) (string, string, error) {
|
|
76
|
-
cmd
|
|
76
|
+
// launchWrap 与流式路径同源:.cmd/.bat 在 Windows 上不能直接 exec,
|
|
77
|
+
// 参数会被 cmd.exe 拆坏(实测含换行的提示词整段丢失)。unix 上原样透传。
|
|
78
|
+
spec := launchWrap(bin, args)
|
|
79
|
+
cmd := exec.Command(spec.bin, spec.args...)
|
|
77
80
|
if dir != "" {
|
|
78
81
|
cmd.Dir = dir
|
|
79
82
|
}
|
|
@@ -147,7 +150,8 @@ func waitDrained(ch <-chan struct{}, n int, d time.Duration) {
|
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
// envWithDefaults 继承当前环境(CLI 依赖 HOME/PATH
|
|
153
|
+
// envWithDefaults 继承当前环境(CLI 依赖 HOME/PATH 等基础变量),
|
|
154
|
+
// 父-side 专属项已在 environ() 阶段过滤(参见 envDenylist)。
|
|
151
155
|
func envWithDefaults() []string {
|
|
152
156
|
return environ()
|
|
153
157
|
}
|
|
@@ -158,8 +162,14 @@ func newStreamCmd(bin string, args []string) *exec.Cmd {
|
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
// newStreamCmdIn 同 newStreamCmd,可指定工作目录(workspace,见 runCLIIn 注释)。
|
|
165
|
+
//
|
|
166
|
+
// ⚠️ 这里的 launchWrap 是 Windows 存活的关键(2026-10-01 实测):
|
|
167
|
+
// npm 装的 claude / codebuddy 是 .cmd 批处理,直接 exec 会让
|
|
168
|
+
// 含换行 / 引号 / & | ^ % 的参数被 cmd.exe 拆坏 → 静默退 0、零输出。
|
|
169
|
+
// unix 上它原样返回,行为不变。见 launch.go。
|
|
161
170
|
func newStreamCmdIn(dir string, bin string, args []string) *exec.Cmd {
|
|
162
|
-
|
|
171
|
+
spec := launchWrap(bin, args)
|
|
172
|
+
cmd := exec.Command(spec.bin, spec.args...)
|
|
163
173
|
if dir != "" {
|
|
164
174
|
cmd.Dir = dir
|
|
165
175
|
}
|
package/internal/agent/runner.go
CHANGED
|
@@ -53,6 +53,8 @@ type Runner struct {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// defaultTimeout 按引擎取默认超时。
|
|
56
|
+
//
|
|
57
|
+
// 2026-10-10 起各引擎统一为 1 小时;未在上面列出的引擎同样回落到 1 小时。
|
|
56
58
|
func (r *Runner) defaultTimeout() time.Duration {
|
|
57
59
|
switch r.Engine.(type) {
|
|
58
60
|
case *ClaudeEngine:
|
|
@@ -71,8 +73,12 @@ func (r *Runner) defaultTimeout() time.Duration {
|
|
|
71
73
|
return DefaultCBGatewayTimeout
|
|
72
74
|
case *DshEngine:
|
|
73
75
|
return DefaultDshTimeout
|
|
76
|
+
case *OpenClawEngine:
|
|
77
|
+
return DefaultOpenClawTimeout
|
|
78
|
+
case *CodexEngine:
|
|
79
|
+
return DefaultCodexTimeout
|
|
74
80
|
}
|
|
75
|
-
return
|
|
81
|
+
return time.Hour
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
// isRetryable 判断错误是否值得重试。
|
|
@@ -252,7 +252,7 @@ func TestRunnerDefaultTimeoutByEngine(t *testing.T) {
|
|
|
252
252
|
{&CodeBuddyEngine{}, DefaultCodeBuddyTimeout},
|
|
253
253
|
{&CodeBuddyAIEngine{}, DefaultCodeBuddyTimeout},
|
|
254
254
|
{&TraeEngine{}, DefaultTraeTimeout},
|
|
255
|
-
{&counterEngine{name: "x"},
|
|
255
|
+
{&counterEngine{name: "x"}, time.Hour},
|
|
256
256
|
}
|
|
257
257
|
for _, c := range cases {
|
|
258
258
|
r := &Runner{Engine: c.e}
|