magic-agent 1.0.109 → 1.0.110
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-ui-mobile/index.html +31 -10
- package/internal/team/planner.go +2 -1
- package/internal/team/role.go +2 -2
- 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 +1 -1
|
@@ -388,10 +388,11 @@ textarea{font:inherit}
|
|
|
388
388
|
/* 环境切换菜单(挂在任务页标题下):已保存的连接 + 「添加连接」。
|
|
389
389
|
与会话行长按菜单同一套浮层范式(scrim 挡住下层,点空白即关)。 */
|
|
390
390
|
.envmenu{position:fixed;z-index:1201;width:min(320px,calc(100vw - 24px));
|
|
391
|
+
max-height:min(70vh,560px);overflow-y:auto;
|
|
391
392
|
background:var(--raised);border:1px solid var(--border);border-radius:18px;
|
|
392
393
|
box-shadow:0 18px 44px -14px var(--app-shadow);padding:6px}
|
|
393
394
|
.envmenu__scrim{position:fixed;inset:0;z-index:1200}
|
|
394
|
-
.envmenu__head{padding:10px 12px 6px;font-size:12px;font-weight:700;letter-spacing:.04em;color:var(--muted)}
|
|
395
|
+
.envmenu__head{display:block;padding:10px 12px 6px;font-size:12px;font-weight:700;letter-spacing:.04em;color:var(--muted)}
|
|
395
396
|
.envmenu__item{display:flex;align-items:center;gap:12px;width:100%;padding:11px 12px;
|
|
396
397
|
border-radius:12px;text-align:left}
|
|
397
398
|
.envmenu__item:active{background:var(--subtle)}
|
|
@@ -405,7 +406,7 @@ textarea{font:inherit}
|
|
|
405
406
|
.envmenu__s{font-size:12px;line-height:16px;color:var(--muted);
|
|
406
407
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
407
408
|
.envmenu__check{flex:0 0 auto;color:var(--primary-action)}
|
|
408
|
-
.envmenu__sep{height:1px;margin:6px 4px;background:var(--border)}
|
|
409
|
+
.envmenu__sep{display:block;height:1px;margin:6px 4px;background:var(--border)}
|
|
409
410
|
.envmenu__add{display:flex;align-items:center;gap:10px;width:100%;padding:12px;border-radius:12px;
|
|
410
411
|
font-size:15px;font-weight:500;color:var(--primary-action);text-align:left}
|
|
411
412
|
.envmenu__add:active{background:var(--subtle)}
|
|
@@ -1250,7 +1251,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1250
1251
|
老 core 没有这个字段 → 空 → 回落到下面这份内置表(与 internal/team 一致)。 */
|
|
1251
1252
|
var TEAM_ROLE_LABELS = {
|
|
1252
1253
|
writer: '写作', researcher: '调研', reviewer: '评审', coder: '编码',
|
|
1253
|
-
designer: '视觉', director: '影像', builder: '
|
|
1254
|
+
designer: '视觉', director: '影像', builder: '游戏'
|
|
1254
1255
|
};
|
|
1255
1256
|
var TEAM_ROLE_BUILTIN = [
|
|
1256
1257
|
{ name: 'writer', kind: 'text', duty: '把材料写成结构清晰、可直接交付的文档' },
|
|
@@ -1259,7 +1260,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1259
1260
|
{ name: 'coder', kind: 'code', duty: '把复杂代码改动落到可构建、可测试的文件上' },
|
|
1260
1261
|
{ name: 'designer', kind: 'image', duty: '产出可直接使用的画面资产' },
|
|
1261
1262
|
{ name: 'director', kind: 'video', duty: '把脚本 / 分镜变成可播放的影像' },
|
|
1262
|
-
{ name: 'builder', kind: 'game', duty: '
|
|
1263
|
+
{ name: 'builder', kind: 'game', duty: '游戏内玩法 / 系统开发,交付能跑起来、可复现启动的工程' }
|
|
1263
1264
|
];
|
|
1264
1265
|
var TEAM_ROLES = []; // core 上报的角色清单;空 = 老 core(或还没连上)→ 用内置表
|
|
1265
1266
|
function teamRoleList() { return TEAM_ROLES.length ? TEAM_ROLES : TEAM_ROLE_BUILTIN; }
|
|
@@ -1359,6 +1360,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1359
1360
|
var CONN_KEY = 'magic.remote.conns.v1', CONN_MAX = 8;
|
|
1360
1361
|
var LS_KEY = 'magic.remote.recent.v1'; // 旧版「最近连接」:只在首次升级时搬一次
|
|
1361
1362
|
var ACTIVE_ID = '';
|
|
1363
|
+
var CONN_SEQ = 0; // 每连一次 +1:上一台桌面迟到的响应不许画到这一台上面
|
|
1362
1364
|
|
|
1363
1365
|
function connHost(url) {
|
|
1364
1366
|
try { return new URL(url).host; }
|
|
@@ -1380,6 +1382,8 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1380
1382
|
host: connHost(c.url), app: '', at: c.at || 0 };
|
|
1381
1383
|
});
|
|
1382
1384
|
saveConns(list);
|
|
1385
|
+
/* 搬完就删旧键:否则用户「清空」后新列表为空,下次又会被旧记录搬回来。 */
|
|
1386
|
+
try { localStorage.removeItem(LS_KEY); } catch (e) {}
|
|
1383
1387
|
}
|
|
1384
1388
|
} catch (e) {}
|
|
1385
1389
|
}
|
|
@@ -1408,7 +1412,10 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1408
1412
|
return next;
|
|
1409
1413
|
}
|
|
1410
1414
|
function removeConn(id) { saveConns(loadConns().filter(function (c) { return c.id !== id; })); }
|
|
1411
|
-
function clearConns() {
|
|
1415
|
+
function clearConns() {
|
|
1416
|
+
saveConns([]);
|
|
1417
|
+
try { localStorage.removeItem(LS_KEY); } catch (e) {}
|
|
1418
|
+
}
|
|
1412
1419
|
|
|
1413
1420
|
/* 当前连接:先按地址 + 令牌找;App 壳注入的连接还没落盘时给一条临时记录,
|
|
1414
1421
|
这样标题与菜单里仍然看得见「现在连的是谁」。 */
|
|
@@ -1467,7 +1474,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1467
1474
|
del.type = 'button';
|
|
1468
1475
|
del.className = 'recent__del';
|
|
1469
1476
|
del.setAttribute('aria-label', '删除连接');
|
|
1470
|
-
del.appendChild(svgUse('i-trash', 18));
|
|
1477
|
+
del.appendChild(svgUse('#i-trash', 18));
|
|
1471
1478
|
del.onclick = function () { removeConn(item.id); renderRecent(); };
|
|
1472
1479
|
row.appendChild(b); row.appendChild(del);
|
|
1473
1480
|
recentListEl.appendChild(row);
|
|
@@ -1520,7 +1527,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1520
1527
|
b.className = 'envmenu__item';
|
|
1521
1528
|
var ico = document.createElement('span');
|
|
1522
1529
|
ico.className = 'envmenu__ico';
|
|
1523
|
-
ico.appendChild(svgUse('i-laptop', 20));
|
|
1530
|
+
ico.appendChild(svgUse('#i-laptop', 20));
|
|
1524
1531
|
b.appendChild(ico);
|
|
1525
1532
|
var col = document.createElement('span');
|
|
1526
1533
|
col.className = 'envmenu__col';
|
|
@@ -1529,12 +1536,16 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1529
1536
|
tl.appendChild(elSpan('envmenu__name', connLabel(c) || c.host || '未命名'));
|
|
1530
1537
|
if (c.app) tl.appendChild(elSpan('envmenu__badge', c.app));
|
|
1531
1538
|
col.appendChild(tl);
|
|
1532
|
-
|
|
1539
|
+
/* 名字就是 host 时不重复写一遍:副标题只留「地址(有名字时)+ 状态」。 */
|
|
1540
|
+
var cname = connLabel(c) || c.host || '';
|
|
1541
|
+
var chost = c.host || connHost(c.url);
|
|
1542
|
+
var csub = (cname && cname !== chost) ? chost : '';
|
|
1543
|
+
col.appendChild(elSpan('envmenu__s', (csub ? csub + ' · ' : '') + (active ? '当前连接' : '点击切换')));
|
|
1533
1544
|
b.appendChild(col);
|
|
1534
1545
|
if (active) {
|
|
1535
1546
|
var ck = document.createElement('span');
|
|
1536
1547
|
ck.className = 'envmenu__check';
|
|
1537
|
-
ck.appendChild(svgUse('i-check', 18));
|
|
1548
|
+
ck.appendChild(svgUse('#i-check', 18));
|
|
1538
1549
|
b.appendChild(ck);
|
|
1539
1550
|
}
|
|
1540
1551
|
b.onclick = function () { closeEnvMenu(); switchEnv(c); };
|
|
@@ -1544,7 +1555,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1544
1555
|
var add = document.createElement('button');
|
|
1545
1556
|
add.type = 'button';
|
|
1546
1557
|
add.className = 'envmenu__add';
|
|
1547
|
-
add.appendChild(svgUse('i-plus', 18));
|
|
1558
|
+
add.appendChild(svgUse('#i-plus', 18));
|
|
1548
1559
|
add.appendChild(elSpan('', '添加连接'));
|
|
1549
1560
|
add.onclick = function () { closeEnvMenu(); showLauncher(); };
|
|
1550
1561
|
envMenuEl.appendChild(add);
|
|
@@ -1643,9 +1654,12 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
1643
1654
|
function refreshMineVersion() {
|
|
1644
1655
|
if (!TOKEN) { paintMineVersion(); return; }
|
|
1645
1656
|
var hdr = { 'X-Magic-Token': TOKEN };
|
|
1657
|
+
var seq = CONN_SEQ; // 切环境后旧桌面的 health 不许写到新连接上
|
|
1646
1658
|
fetch(BASE + '/desk/health', { headers: hdr })
|
|
1647
1659
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
1648
1660
|
.then(function (h) {
|
|
1661
|
+
if (seq !== CONN_SEQ) return;
|
|
1662
|
+
if (h && h.app) noteActiveMeta({ app: h.app });
|
|
1649
1663
|
if (h && typeof h.version === 'string' && h.version) { SERVER_VERSION = h.version; return; }
|
|
1650
1664
|
return fetch(BASE + '/desk/contract', { headers: hdr })
|
|
1651
1665
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
@@ -2214,6 +2228,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
2214
2228
|
function connect(target) {
|
|
2215
2229
|
BASE = target.url || '';
|
|
2216
2230
|
TOKEN = target.token || '';
|
|
2231
|
+
CONN_SEQ++; // 上一台桌面还在飞的请求:回来时直接丢弃(见 loadSessions)
|
|
2217
2232
|
var c = upsertConn(target);
|
|
2218
2233
|
ACTIVE_ID = c.id;
|
|
2219
2234
|
showError('');
|
|
@@ -2224,6 +2239,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
2224
2239
|
function disconnect() {
|
|
2225
2240
|
stopPoll();
|
|
2226
2241
|
closeEnvMenu();
|
|
2242
|
+
CONN_SEQ++;
|
|
2227
2243
|
if (conv) { try { conv.destroy(); } catch (e) {} conv = null; }
|
|
2228
2244
|
convEl.innerHTML = '';
|
|
2229
2245
|
BASE = ''; TOKEN = '';
|
|
@@ -2273,9 +2289,11 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
2273
2289
|
if (historyEl.hidden) return; // 只在这张页可见时拉
|
|
2274
2290
|
if (!TOKEN) return; // 还没配对,拉也拉不到
|
|
2275
2291
|
var hdr = TOKEN ? { 'X-Magic-Token': TOKEN } : {};
|
|
2292
|
+
var seq = CONN_SEQ;
|
|
2276
2293
|
fetch(BASE + '/desk/sessions', { headers: hdr })
|
|
2277
2294
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
2278
2295
|
.then(function (list) {
|
|
2296
|
+
if (seq !== CONN_SEQ) return; // 响应回来时已经切到另一台桌面了
|
|
2279
2297
|
if (!Array.isArray(list)) return;
|
|
2280
2298
|
if (sessionSig(list) === sessionSig(LAST_SESSIONS)) return; // 没变 → 不动 DOM
|
|
2281
2299
|
loadSessions(true); // 变了才走完整刷新(含标题归纳 / 健康),且不许闪
|
|
@@ -2284,6 +2302,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
2284
2302
|
}
|
|
2285
2303
|
function loadSessions(quiet) {
|
|
2286
2304
|
var hdr = TOKEN ? { 'X-Magic-Token': TOKEN } : {};
|
|
2305
|
+
var seq = CONN_SEQ; // 切环境后旧请求可能才回来:序号对不上就不许改这一台的画面
|
|
2287
2306
|
/* quiet(由 startListPoll 的轮询触发):不塞「读取中…」占位、失败也不覆盖现有列表 ——
|
|
2288
2307
|
轮询是**后台行为**,不能因为一次网络抖动就把用户正看着的列表换成一行错误,
|
|
2289
2308
|
也不该让他每次看到整页闪一下「读取中…」(这正是 deleteSessionRow 绕开它的原因)。 */
|
|
@@ -2299,6 +2318,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
2299
2318
|
var pDirs = fetch(BASE + '/desk/dirs', { headers: hdr })
|
|
2300
2319
|
.then(function (r) { return r.ok ? r.json() : {}; }).catch(function () { return {}; });
|
|
2301
2320
|
Promise.all([pList, pTitles, pHealth, pDirs]).then(function (a) {
|
|
2321
|
+
if (seq !== CONN_SEQ) return;
|
|
2302
2322
|
TITLES = a[1] || {};
|
|
2303
2323
|
DW = (a[2] && a[2].defaultWorkspace) || '';
|
|
2304
2324
|
DW_READY = true;
|
|
@@ -2332,6 +2352,7 @@ body.is-conv .tabfab__b{width:56px;height:56px;bottom:calc(env(safe-area-inset-b
|
|
|
2332
2352
|
var pending = LAST_SESSIONS.filter(function (s) { return !s.title_summarized; });
|
|
2333
2353
|
pending.slice(0, TITLE_SUMMARIZE_CAP).forEach(function (s) { setTimeout(function () { ensureTitle(s); }, 0); });
|
|
2334
2354
|
}).catch(function (e) {
|
|
2355
|
+
if (seq !== CONN_SEQ) return;
|
|
2335
2356
|
if (quiet) return; // 后台轮询失败:保留现有列表(见上面的说明)
|
|
2336
2357
|
historyList.innerHTML = '<div class="history__hint">读取会话失败:' + (e && e.message ? e.message : e) + '</div>';
|
|
2337
2358
|
});
|
package/internal/team/planner.go
CHANGED
|
@@ -103,7 +103,8 @@ const plannerPromptTemplate = `你是任务规划器。把用户的诉求拆成
|
|
|
103
103
|
- code:代码产出(脚本、程序、构建 / 测试相关的改动)→ coder。
|
|
104
104
|
- image:画面类产出(位图 / 矢量:封面、插画、海报、图标)→ designer。
|
|
105
105
|
- video:影像类产出(分镜、剪辑、成片)→ director。
|
|
106
|
-
- game
|
|
106
|
+
- game:游戏相关产出 —— 游戏内玩法 / 系统开发(在现有工程里改代码、加功能),以及从零交付
|
|
107
|
+
可运行的游戏工程(代码 + 素材 + 启动方式)→ builder。
|
|
107
108
|
- {{CODE_GATE}}
|
|
108
109
|
- **需求里出现了对应类别的产物,就必须单独拆出该类任务**,哪怕用户没明说「配图 / 做成视频」。
|
|
109
110
|
例:「写 100 字游戏介绍并配 1 个封面」→ 1 个 writer + 1 个 designer;
|
package/internal/team/role.go
CHANGED
|
@@ -87,8 +87,8 @@ var builtinRoles = []Role{
|
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
89
|
Name: "builder",
|
|
90
|
-
Duty: "
|
|
91
|
-
SystemPrompt: "
|
|
90
|
+
Duty: "游戏内玩法 / 系统开发,交付能跑起来、可复现启动的工程",
|
|
91
|
+
SystemPrompt: "你的角色是游戏开发者。在现有工程里做玩法 / 系统开发时,先读懂现有结构与约定,只改相关范围;从零交付工程时,先搭最小可运行骨架再补功能。所有命令都要在工作目录内可复现,依赖与启动方式写进交付物。",
|
|
92
92
|
Kind: KindGame,
|
|
93
93
|
},
|
|
94
94
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|