@kevin5251984/guild 0.2.18 → 0.2.20
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/package.json +2 -2
- package/src/catalog/subagents.ts +2 -1
- package/src/chat-parts.ts +6 -1
- package/src/cli.ts +2 -5
- package/src/compact.ts +27 -0
- package/src/db.ts +236 -68
- package/src/generate.ts +59 -28
- package/src/handlers.ts +355 -84
- package/src/harness.ts +39 -10
- package/src/host-browse.ts +149 -4
- package/src/image-gen.ts +2 -1
- package/src/llm.ts +435 -71
- package/src/mcp.ts +32 -5
- package/src/memory.ts +69 -0
- package/src/mention.ts +189 -14
- package/src/oauth.ts +204 -27
- package/src/opencode-free.ts +248 -0
- package/src/public/buddy.js +432 -0
- package/src/public/chat.css +174 -72
- package/src/public/chat.html +354 -69
- package/src/public/i18n.js +44 -6
- package/src/public/index.html +1 -46
- package/src/public/mobile.css +597 -0
- package/src/public/mobile.html +954 -0
- package/src/public/settings.html +239 -35
- package/src/public/skills-add.html +8 -0
- package/src/public/studio.html +171 -117
- package/src/public/style.css +220 -26
- package/src/public/subagents-add.html +8 -0
- package/src/reasoning-catalog.ts +346 -0
- package/src/router.ts +184 -13
- package/src/store.ts +170 -5
- package/src/subagent.ts +239 -7
- package/src/tools.ts +132 -21
- package/src/trajectory.ts +72 -9
- package/src/usage.ts +10 -0
- package/src/version.ts +25 -0
- package/vendor/protocol/src/index.ts +27 -2
package/src/public/chat.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
|
|
9
9
|
<link rel="icon" href="/favicon-16.png" type="image/png" sizes="16x16" />
|
|
10
10
|
<title>Guild — 大廳</title>
|
|
11
|
-
<link rel="stylesheet" href="/chat.css?v=
|
|
11
|
+
<link rel="stylesheet" href="/chat.css?v=quest-nest" />
|
|
12
12
|
<script src="/i18n.js"></script>
|
|
13
13
|
<script src="/md.js"></script>
|
|
14
14
|
</head>
|
|
@@ -167,12 +167,7 @@
|
|
|
167
167
|
<a class="model-choice" href="/settings" data-i18n="model.settings">狀態欄</a>
|
|
168
168
|
</div>
|
|
169
169
|
</div>
|
|
170
|
-
<select id="model-reasoning" hidden>
|
|
171
|
-
<option value="minimal" data-i18n="reason.min">最低</option>
|
|
172
|
-
<option value="low" data-i18n="reason.low">低</option>
|
|
173
|
-
<option value="medium" data-i18n="reason.mid">中</option>
|
|
174
|
-
<option value="high" data-i18n="reason.high">高</option>
|
|
175
|
-
</select>
|
|
170
|
+
<select id="model-reasoning" hidden></select>
|
|
176
171
|
<input type="checkbox" id="model-fast" hidden />
|
|
177
172
|
</div>
|
|
178
173
|
</div>
|
|
@@ -266,6 +261,13 @@
|
|
|
266
261
|
</div>
|
|
267
262
|
<script>
|
|
268
263
|
const COLORS = ["#7c6af7", "#5b8def", "#2ea887", "#e07a3d", "#d4537e"];
|
|
264
|
+
const SEAT_HUE = {
|
|
265
|
+
infra: "#5b8def",
|
|
266
|
+
pm: "#2ea887",
|
|
267
|
+
rd: "#e07a3d",
|
|
268
|
+
design: "#7c6af7",
|
|
269
|
+
marketing: "#d4537e",
|
|
270
|
+
};
|
|
269
271
|
const CHANNEL_ROSTER_CAP = 6;
|
|
270
272
|
function isGeneralChannel(ch) {
|
|
271
273
|
return Boolean(ch && (ch.id === "channel-general" || ch.name === "general"));
|
|
@@ -321,6 +323,8 @@
|
|
|
321
323
|
};
|
|
322
324
|
|
|
323
325
|
function hue(key) {
|
|
326
|
+
const seat = SEAT_HUE[String(key).toLowerCase()];
|
|
327
|
+
if (seat) return seat;
|
|
324
328
|
let n = 0;
|
|
325
329
|
for (const ch of String(key)) n = (n + ch.charCodeAt(0) * 17) % COLORS.length;
|
|
326
330
|
return COLORS[n];
|
|
@@ -341,6 +345,15 @@
|
|
|
341
345
|
const glyph = Array.from(String(name).replace(/\s+/g, ""))[0] || "?";
|
|
342
346
|
return glyph;
|
|
343
347
|
}
|
|
348
|
+
function portraitUrl(bot) {
|
|
349
|
+
const url = bot && typeof bot.portrait === "string" ? bot.portrait.trim() : "";
|
|
350
|
+
return url.indexOf("/generated/") === 0 ? url : "";
|
|
351
|
+
}
|
|
352
|
+
function avatarFace(bot) {
|
|
353
|
+
const src = portraitUrl(bot);
|
|
354
|
+
if (src) return '<img alt="" src="' + escapeHtml(src) + '">';
|
|
355
|
+
return escapeHtml(initials((bot && (bot.name || bot.handle)) || "?"));
|
|
356
|
+
}
|
|
344
357
|
function escapeHtml(value) {
|
|
345
358
|
return String(value)
|
|
346
359
|
.replace(/&/g, "&")
|
|
@@ -1027,14 +1040,48 @@
|
|
|
1027
1040
|
}
|
|
1028
1041
|
return out;
|
|
1029
1042
|
}
|
|
1043
|
+
function deferredHandles(text) {
|
|
1044
|
+
const raw = String(text || "");
|
|
1045
|
+
const out = [];
|
|
1046
|
+
const add = (name) => {
|
|
1047
|
+
const key = String(name || "").toLowerCase();
|
|
1048
|
+
if (key && out.indexOf(key) < 0) out.push(key);
|
|
1049
|
+
};
|
|
1050
|
+
const scan = raw.replace(/```[\s\S]*?```/g, " ");
|
|
1051
|
+
const cue =
|
|
1052
|
+
/(?:完成後|通過後|最後(?:才|由)?|才需要|才叫|才交)[^@\n]{0,24}@([A-Za-z0-9_-]+)/gi;
|
|
1053
|
+
const bare =
|
|
1054
|
+
/(?:完成後|才需要|才叫)\s*(?:call|ask|由)\s*`?@?([A-Za-z0-9_-]+)`?/gi;
|
|
1055
|
+
let row;
|
|
1056
|
+
while ((row = cue.exec(scan))) add(row[1]);
|
|
1057
|
+
while ((row = bare.exec(scan))) add(row[1]);
|
|
1058
|
+
const later =
|
|
1059
|
+
/^[ \t]*(?:\d+[.)、]\s*|[-*•]\s+)(?:通過後|最後|然後|之後|完成後)\s*`?@([A-Za-z0-9_-]+)`?/i;
|
|
1060
|
+
const lines = raw.split(/\r?\n/);
|
|
1061
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1062
|
+
const hit = lines[i].match(later);
|
|
1063
|
+
if (hit) add(hit[1]);
|
|
1064
|
+
}
|
|
1065
|
+
return out;
|
|
1066
|
+
}
|
|
1030
1067
|
function summonedBotIds(text) {
|
|
1031
1068
|
const raw = String(text || "");
|
|
1069
|
+
const deferred = new Set(deferredHandles(raw));
|
|
1070
|
+
const members = new Set(
|
|
1071
|
+
state.kind === "channel"
|
|
1072
|
+
? ((currentChannel() && currentChannel().memberIds) || [])
|
|
1073
|
+
: state.kind === "dm"
|
|
1074
|
+
? [state.id]
|
|
1075
|
+
: [],
|
|
1076
|
+
);
|
|
1032
1077
|
const known = new Map(
|
|
1033
|
-
(state.bots || [])
|
|
1078
|
+
(state.bots || [])
|
|
1079
|
+
.filter((bot) => !members.size || members.has(bot.id))
|
|
1080
|
+
.map((bot) => [String(bot.handle).toLowerCase(), bot.id]),
|
|
1034
1081
|
);
|
|
1035
1082
|
const out = [];
|
|
1036
|
-
const push = (id) => {
|
|
1037
|
-
if (id && !out.includes(id)) out.push(id);
|
|
1083
|
+
const push = (id, handle) => {
|
|
1084
|
+
if (id && !deferred.has(handle) && !out.includes(id)) out.push(id);
|
|
1038
1085
|
};
|
|
1039
1086
|
let fence = false;
|
|
1040
1087
|
const lines = raw.split(/\r?\n/);
|
|
@@ -1051,11 +1098,21 @@
|
|
|
1051
1098
|
if (!m) continue;
|
|
1052
1099
|
const names = m[1].match(/@([A-Za-z0-9_-]+)/g) || [];
|
|
1053
1100
|
for (let n = 0; n < names.length; n++) {
|
|
1054
|
-
|
|
1101
|
+
const handle = names[n].slice(1).toLowerCase();
|
|
1102
|
+
push(known.get(handle), handle);
|
|
1055
1103
|
}
|
|
1056
1104
|
}
|
|
1057
1105
|
if (out.length) return out;
|
|
1058
|
-
return mentionedBotIds(raw)
|
|
1106
|
+
return mentionedBotIds(raw)
|
|
1107
|
+
.filter((id) => {
|
|
1108
|
+
const bot = (state.bots || []).find((row) => row.id === id);
|
|
1109
|
+
return (
|
|
1110
|
+
bot &&
|
|
1111
|
+
(!members.size || members.has(id)) &&
|
|
1112
|
+
!deferred.has(String(bot.handle).toLowerCase())
|
|
1113
|
+
);
|
|
1114
|
+
})
|
|
1115
|
+
.slice(0, 1);
|
|
1059
1116
|
}
|
|
1060
1117
|
function lastBotAuthor() {
|
|
1061
1118
|
const msgs = state.messages || [];
|
|
@@ -1128,6 +1185,12 @@
|
|
|
1128
1185
|
hour12: true,
|
|
1129
1186
|
});
|
|
1130
1187
|
}
|
|
1188
|
+
function formatMsgWhen(iso) {
|
|
1189
|
+
const day = formatMsgDay(iso);
|
|
1190
|
+
const clock = formatMsgClock(iso);
|
|
1191
|
+
if (day && clock) return day + " " + clock;
|
|
1192
|
+
return clock || day || "";
|
|
1193
|
+
}
|
|
1131
1194
|
function formatMsgDay(iso) {
|
|
1132
1195
|
if (!iso) return "";
|
|
1133
1196
|
const when = new Date(iso);
|
|
@@ -1229,7 +1292,20 @@
|
|
|
1229
1292
|
}
|
|
1230
1293
|
function botModelMeta(bot) {
|
|
1231
1294
|
const model = botModelLabel(bot) || "—";
|
|
1232
|
-
const
|
|
1295
|
+
const spec = (() => {
|
|
1296
|
+
const ref = botModelRef(bot);
|
|
1297
|
+
if (!ref) return null;
|
|
1298
|
+
const hit = allModels().find(
|
|
1299
|
+
(row) => row.provider === ref.provider && row.model === ref.model,
|
|
1300
|
+
);
|
|
1301
|
+
return (hit && hit.reasoning) || null;
|
|
1302
|
+
})();
|
|
1303
|
+
const reasonVal = clampEffort(
|
|
1304
|
+
modelCfg && modelCfg.reasoning,
|
|
1305
|
+
spec,
|
|
1306
|
+
Boolean(modelCfg && modelCfg.fast),
|
|
1307
|
+
);
|
|
1308
|
+
const reason = reasonVal ? reasonLabel(reasonVal) : "—";
|
|
1233
1309
|
const provider = botProviderLabel(bot) || "—";
|
|
1234
1310
|
return model + " · " + reason + " · " + provider;
|
|
1235
1311
|
}
|
|
@@ -1266,11 +1342,15 @@
|
|
|
1266
1342
|
const foot = opts.party
|
|
1267
1343
|
? opts.party
|
|
1268
1344
|
: "<em>" + escapeHtml(opts.preview || "") + "</em>";
|
|
1269
|
-
|
|
1270
|
-
|
|
1345
|
+
const cls =
|
|
1346
|
+
"nav-row" +
|
|
1271
1347
|
(opts.on ? " on" : "") +
|
|
1272
1348
|
(opts.unread ? " unread" : "") +
|
|
1273
1349
|
(opts.notice ? " notice" : "") +
|
|
1350
|
+
(opts.branch ? " nav-branch" : "");
|
|
1351
|
+
const inner =
|
|
1352
|
+
'<a class="' +
|
|
1353
|
+
cls +
|
|
1274
1354
|
'" href="' +
|
|
1275
1355
|
opts.href +
|
|
1276
1356
|
'">' +
|
|
@@ -1282,8 +1362,9 @@
|
|
|
1282
1362
|
right +
|
|
1283
1363
|
"</span>" +
|
|
1284
1364
|
foot +
|
|
1285
|
-
"</span></a
|
|
1286
|
-
);
|
|
1365
|
+
"</span></a>";
|
|
1366
|
+
if (opts.bare) return inner;
|
|
1367
|
+
return "<li>" + inner + "</li>";
|
|
1287
1368
|
}
|
|
1288
1369
|
function partyStackHtml(members) {
|
|
1289
1370
|
const bots = Array.isArray(members) ? members : [];
|
|
@@ -1293,7 +1374,7 @@
|
|
|
1293
1374
|
'<span class="avatar" style="background:' +
|
|
1294
1375
|
hue(bot.handle) +
|
|
1295
1376
|
'">' +
|
|
1296
|
-
|
|
1377
|
+
avatarFace(bot) +
|
|
1297
1378
|
"</span>",
|
|
1298
1379
|
)
|
|
1299
1380
|
.join("");
|
|
@@ -1343,31 +1424,93 @@
|
|
|
1343
1424
|
}
|
|
1344
1425
|
function renderNav() {
|
|
1345
1426
|
const q = document.getElementById("filter").value.toLowerCase();
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1427
|
+
const all = state.channels || [];
|
|
1428
|
+
const byId = new Map(all.map((ch) => [ch.id, ch]));
|
|
1429
|
+
const kidsOf = (id) =>
|
|
1430
|
+
all.filter((ch) => ch.parentId === id).slice().sort(byUpdatedAtDesc);
|
|
1431
|
+
const visible = new Set();
|
|
1432
|
+
for (const ch of all) {
|
|
1433
|
+
const hit = !q || String(ch.name || "").toLowerCase().includes(q);
|
|
1434
|
+
if (!hit) continue;
|
|
1435
|
+
let walk = ch;
|
|
1436
|
+
while (walk) {
|
|
1437
|
+
visible.add(walk.id);
|
|
1438
|
+
walk = walk.parentId ? byId.get(walk.parentId) : null;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
function channelBusy(ch) {
|
|
1442
|
+
return Boolean(
|
|
1443
|
+
(state.busyRoom &&
|
|
1444
|
+
state.busyRoom.kind === "channel" &&
|
|
1445
|
+
state.busyRoom.id === ch.id) ||
|
|
1446
|
+
(state.serverLive || []).some(
|
|
1447
|
+
(row) => row.kind === "channel" && row.id === ch.id,
|
|
1448
|
+
),
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
function channelUpdated(ch) {
|
|
1452
|
+
let latest = ch.updatedAt ? Date.parse(ch.updatedAt) : 0;
|
|
1453
|
+
for (const kid of kidsOf(ch.id)) {
|
|
1454
|
+
const child = channelUpdated(kid);
|
|
1455
|
+
const ts = child ? Date.parse(child) : 0;
|
|
1456
|
+
if (ts > latest) latest = ts;
|
|
1457
|
+
}
|
|
1458
|
+
return Number.isFinite(latest) && latest > 0
|
|
1459
|
+
? new Date(latest).toISOString()
|
|
1460
|
+
: ch.updatedAt;
|
|
1461
|
+
}
|
|
1462
|
+
function branchTree(parentId) {
|
|
1463
|
+
return kidsOf(parentId)
|
|
1464
|
+
.filter((ch) => visible.has(ch.id))
|
|
1465
|
+
.map((ch) => {
|
|
1466
|
+
const nested = branchTree(ch.id);
|
|
1467
|
+
const row = navRowHtml({
|
|
1468
|
+
bare: true,
|
|
1469
|
+
branch: true,
|
|
1470
|
+
href: "#c/" + encodeURIComponent(ch.id),
|
|
1471
|
+
on: state.kind === "channel" && state.id === ch.id,
|
|
1472
|
+
notice: true,
|
|
1473
|
+
name: "↳ " + ch.name,
|
|
1474
|
+
updatedAt: ch.updatedAt,
|
|
1475
|
+
party: partyStackHtml(ch.members),
|
|
1476
|
+
busy: channelBusy(ch),
|
|
1477
|
+
unread: state.unread.has(roomKey("channel", ch.id)),
|
|
1478
|
+
});
|
|
1479
|
+
return nested
|
|
1480
|
+
? "<li>" + row + '<ul class="nav-branches">' + nested + "</ul></li>"
|
|
1481
|
+
: "<li>" + row + "</li>";
|
|
1482
|
+
})
|
|
1483
|
+
.join("");
|
|
1484
|
+
}
|
|
1485
|
+
const roots = all
|
|
1486
|
+
.filter((ch) => !ch.parentId && visible.has(ch.id))
|
|
1348
1487
|
.slice()
|
|
1349
|
-
.sort(
|
|
1350
|
-
|
|
1488
|
+
.sort((a, b) =>
|
|
1489
|
+
byUpdatedAtDesc(
|
|
1490
|
+
{ updatedAt: channelUpdated(a) },
|
|
1491
|
+
{ updatedAt: channelUpdated(b) },
|
|
1492
|
+
),
|
|
1493
|
+
);
|
|
1494
|
+
document.getElementById("channels").innerHTML = roots
|
|
1351
1495
|
.map((ch) => {
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
state.busyRoom.id === ch.id) ||
|
|
1356
|
-
(state.serverLive || []).some(
|
|
1357
|
-
(row) => row.kind === "channel" && row.id === ch.id,
|
|
1358
|
-
),
|
|
1359
|
-
);
|
|
1360
|
-
const unread = state.unread.has(roomKey("channel", ch.id));
|
|
1361
|
-
return navRowHtml({
|
|
1496
|
+
const nested = branchTree(ch.id);
|
|
1497
|
+
const row = navRowHtml({
|
|
1498
|
+
bare: true,
|
|
1362
1499
|
href: "#c/" + encodeURIComponent(ch.id),
|
|
1363
1500
|
on: state.kind === "channel" && state.id === ch.id,
|
|
1364
1501
|
notice: true,
|
|
1365
1502
|
name: ch.name,
|
|
1366
|
-
updatedAt: ch
|
|
1503
|
+
updatedAt: channelUpdated(ch),
|
|
1367
1504
|
party: partyStackHtml(ch.members),
|
|
1368
|
-
busy:
|
|
1369
|
-
unread: unread,
|
|
1505
|
+
busy: channelBusy(ch),
|
|
1506
|
+
unread: state.unread.has(roomKey("channel", ch.id)),
|
|
1370
1507
|
});
|
|
1508
|
+
return (
|
|
1509
|
+
'<li class="nav-quest">' +
|
|
1510
|
+
row +
|
|
1511
|
+
(nested ? '<ul class="nav-branches">' + nested + "</ul>" : "") +
|
|
1512
|
+
"</li>"
|
|
1513
|
+
);
|
|
1371
1514
|
})
|
|
1372
1515
|
.join("");
|
|
1373
1516
|
const bots = state.bots
|
|
@@ -1391,7 +1534,7 @@
|
|
|
1391
1534
|
'<span class="avatar" style="background:' +
|
|
1392
1535
|
hue(bot.handle) +
|
|
1393
1536
|
'">' +
|
|
1394
|
-
|
|
1537
|
+
avatarFace(bot) +
|
|
1395
1538
|
"</span>",
|
|
1396
1539
|
name: bot.name,
|
|
1397
1540
|
updatedAt: bot.updatedAt,
|
|
@@ -1426,7 +1569,7 @@
|
|
|
1426
1569
|
'" style="background:' +
|
|
1427
1570
|
hue(bot.handle) +
|
|
1428
1571
|
'">' +
|
|
1429
|
-
|
|
1572
|
+
avatarFace(bot) +
|
|
1430
1573
|
'</button><span class="who"><strong>' +
|
|
1431
1574
|
escapeHtml(bot.name) +
|
|
1432
1575
|
"</strong><em>@" +
|
|
@@ -1445,9 +1588,9 @@
|
|
|
1445
1588
|
document.getElementById("subtitle").textContent = "";
|
|
1446
1589
|
wrap.hidden = true;
|
|
1447
1590
|
document.getElementById("members-pop").hidden = true;
|
|
1448
|
-
av.textContent = bot ? initials(bot.name) : "?";
|
|
1449
1591
|
av.style.background = bot ? hue(bot.handle) : "#333";
|
|
1450
1592
|
av.classList.remove("hash-av");
|
|
1593
|
+
av.innerHTML = bot ? avatarFace(bot) : "?";
|
|
1451
1594
|
if (bot) {
|
|
1452
1595
|
av.setAttribute("href", "#");
|
|
1453
1596
|
av.setAttribute("data-bot-id", bot.id);
|
|
@@ -1480,7 +1623,7 @@
|
|
|
1480
1623
|
'<span class="avatar" style="background:' +
|
|
1481
1624
|
hue(bot.handle) +
|
|
1482
1625
|
'">' +
|
|
1483
|
-
|
|
1626
|
+
avatarFace(bot) +
|
|
1484
1627
|
"</span>",
|
|
1485
1628
|
)
|
|
1486
1629
|
.join("");
|
|
@@ -1624,7 +1767,7 @@
|
|
|
1624
1767
|
'" style="background:' +
|
|
1625
1768
|
hue(bot.handle) +
|
|
1626
1769
|
'">' +
|
|
1627
|
-
|
|
1770
|
+
avatarFace(bot) +
|
|
1628
1771
|
"</button>",
|
|
1629
1772
|
head:
|
|
1630
1773
|
'<div class="msg-head"><button type="button" class="name"' +
|
|
@@ -1848,23 +1991,28 @@
|
|
|
1848
1991
|
rail.innerHTML = you
|
|
1849
1992
|
.map((el) => {
|
|
1850
1993
|
const id = el.getAttribute("data-id") || "";
|
|
1994
|
+
const when = formatMsgWhen(el.getAttribute("data-at") || "");
|
|
1851
1995
|
const preview = youPromptPreview(el);
|
|
1852
1996
|
const top = Math.max(
|
|
1853
1997
|
6,
|
|
1854
1998
|
Math.min(track - 6, ((el.offsetTop + el.offsetHeight / 2) / total) * track),
|
|
1855
1999
|
);
|
|
2000
|
+
const label = [when, preview].filter(Boolean).join(" · ");
|
|
1856
2001
|
return (
|
|
1857
2002
|
'<button type="button" class="prompt-tick" data-prompt-jump="' +
|
|
1858
2003
|
escapeHtml(id) +
|
|
1859
2004
|
'" style="top:' +
|
|
1860
2005
|
top +
|
|
1861
|
-
'px"' +
|
|
2006
|
+
'px" aria-label="' +
|
|
2007
|
+
escapeHtml(t("promptJump") + (label ? " · " + label : "")) +
|
|
2008
|
+
'"><span class="prompt-tip">' +
|
|
2009
|
+
(when
|
|
2010
|
+
? '<time class="prompt-tip-when">' + escapeHtml(when) + "</time>"
|
|
2011
|
+
: "") +
|
|
1862
2012
|
(preview
|
|
1863
|
-
? '
|
|
2013
|
+
? '<span class="prompt-tip-text">' + escapeHtml(preview) + "</span>"
|
|
1864
2014
|
: "") +
|
|
1865
|
-
|
|
1866
|
-
escapeHtml(t("promptJump") + (preview ? " · " + preview : "")) +
|
|
1867
|
-
'"></button>'
|
|
2015
|
+
"</span></button>"
|
|
1868
2016
|
);
|
|
1869
2017
|
})
|
|
1870
2018
|
.join("");
|
|
@@ -1988,6 +2136,15 @@
|
|
|
1988
2136
|
'">' +
|
|
1989
2137
|
t("retry") +
|
|
1990
2138
|
"</button>" +
|
|
2139
|
+
(state.kind === "channel" &&
|
|
2140
|
+
!msg.queued &&
|
|
2141
|
+
String(msg.id || "").indexOf("pending-") !== 0
|
|
2142
|
+
? '<button type="button" data-branch="' +
|
|
2143
|
+
escapeHtml(msg.id) +
|
|
2144
|
+
'">' +
|
|
2145
|
+
t("branch") +
|
|
2146
|
+
"</button>"
|
|
2147
|
+
: "") +
|
|
1991
2148
|
(msg.queued
|
|
1992
2149
|
? ""
|
|
1993
2150
|
: '<button type="button" class="danger" data-msg-del="' +
|
|
@@ -2003,6 +2160,8 @@
|
|
|
2003
2160
|
(msg.queued ? " queued" : msg.steer ? " steer" : "") +
|
|
2004
2161
|
'" data-id="' +
|
|
2005
2162
|
escapeHtml(msg.id) +
|
|
2163
|
+
'" data-at="' +
|
|
2164
|
+
escapeHtml(stamp) +
|
|
2006
2165
|
'">' +
|
|
2007
2166
|
clock +
|
|
2008
2167
|
body +
|
|
@@ -2021,7 +2180,7 @@
|
|
|
2021
2180
|
'" style="background:' +
|
|
2022
2181
|
hue(handle || name) +
|
|
2023
2182
|
'">' +
|
|
2024
|
-
escapeHtml(initials(name)) +
|
|
2183
|
+
(bot ? avatarFace(bot) : escapeHtml(initials(name))) +
|
|
2025
2184
|
"</button>";
|
|
2026
2185
|
const head =
|
|
2027
2186
|
'<div class="msg-head"><button type="button" class="name"' +
|
|
@@ -2122,6 +2281,30 @@
|
|
|
2122
2281
|
await loadWorkspace();
|
|
2123
2282
|
if (state.kind === "channel") await loadMessages();
|
|
2124
2283
|
}
|
|
2284
|
+
async function closeBranch(id, ch) {
|
|
2285
|
+
if (!id) return;
|
|
2286
|
+
if (!confirm(t("branch.closeConfirm", { name: (ch && ch.name) || id }))) {
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
const merge = confirm(t("branch.closeMerge"));
|
|
2290
|
+
const res = await fetch(
|
|
2291
|
+
"/channels/" + encodeURIComponent(id) + "/close",
|
|
2292
|
+
{
|
|
2293
|
+
method: "POST",
|
|
2294
|
+
headers: { "content-type": "application/json" },
|
|
2295
|
+
body: JSON.stringify({ merge: merge }),
|
|
2296
|
+
},
|
|
2297
|
+
);
|
|
2298
|
+
const body = await res.json().catch(() => ({}));
|
|
2299
|
+
if (!res.ok) throw new Error(body.error || t("branch.closeFailed"));
|
|
2300
|
+
const parentId = (ch && ch.parentId) || body.parentId || "channel-general";
|
|
2301
|
+
if (state.kind === "channel" && state.id === id) {
|
|
2302
|
+
state.id = parentId;
|
|
2303
|
+
setHash();
|
|
2304
|
+
}
|
|
2305
|
+
await loadWorkspace();
|
|
2306
|
+
if (state.kind === "channel") await loadMessages();
|
|
2307
|
+
}
|
|
2125
2308
|
|
|
2126
2309
|
|
|
2127
2310
|
function roomMessagesUrl(kind, id, suffix) {
|
|
@@ -2471,6 +2654,29 @@
|
|
|
2471
2654
|
renderThread();
|
|
2472
2655
|
renderNav();
|
|
2473
2656
|
}
|
|
2657
|
+
async function branchFromMessage(id) {
|
|
2658
|
+
if (state.kind !== "channel" || !state.id) return;
|
|
2659
|
+
const msg = state.messages.find((item) => item.id === id);
|
|
2660
|
+
if (!msg) return;
|
|
2661
|
+
const def = String(msg.body || "")
|
|
2662
|
+
.replace(/\s+/g, " ")
|
|
2663
|
+
.trim()
|
|
2664
|
+
.slice(0, 28);
|
|
2665
|
+
const typed = window.prompt(t("branch.name"), def || "");
|
|
2666
|
+
if (typed === null) return;
|
|
2667
|
+
const name = typed.trim() || def;
|
|
2668
|
+
const res = await fetch(
|
|
2669
|
+
"/channels/" + encodeURIComponent(state.id) + "/branches",
|
|
2670
|
+
{
|
|
2671
|
+
method: "POST",
|
|
2672
|
+
headers: { "content-type": "application/json" },
|
|
2673
|
+
body: JSON.stringify({ messageId: id, name: name || undefined }),
|
|
2674
|
+
},
|
|
2675
|
+
);
|
|
2676
|
+
const body = await res.json().catch(() => ({}));
|
|
2677
|
+
if (!res.ok) throw new Error(body.error || "branch failed");
|
|
2678
|
+
location.hash = "#c/" + encodeURIComponent(body.id);
|
|
2679
|
+
}
|
|
2474
2680
|
async function retryMessage(id, body) {
|
|
2475
2681
|
if (canStopHere()) {
|
|
2476
2682
|
stopTurn();
|
|
@@ -2499,6 +2705,7 @@
|
|
|
2499
2705
|
const payload = {};
|
|
2500
2706
|
if (typeof body === "string") payload.body = body;
|
|
2501
2707
|
if (botIds.length === 1) payload.assigneeId = botIds[0];
|
|
2708
|
+
if (botIds.length) payload.mentions = botIds;
|
|
2502
2709
|
await postChat(
|
|
2503
2710
|
messagesUrl(id) + "/retry",
|
|
2504
2711
|
payload,
|
|
@@ -2688,6 +2895,12 @@
|
|
|
2688
2895
|
await retryMessage(retry.getAttribute("data-retry"));
|
|
2689
2896
|
return;
|
|
2690
2897
|
}
|
|
2898
|
+
const branchBtn = event.target.closest("[data-branch]");
|
|
2899
|
+
if (branchBtn) {
|
|
2900
|
+
event.preventDefault();
|
|
2901
|
+
await branchFromMessage(branchBtn.getAttribute("data-branch"));
|
|
2902
|
+
return;
|
|
2903
|
+
}
|
|
2691
2904
|
const del = event.target.closest("[data-msg-del]");
|
|
2692
2905
|
if (del) {
|
|
2693
2906
|
event.preventDefault();
|
|
@@ -2807,7 +3020,11 @@
|
|
|
2807
3020
|
const nameEl = document.getElementById("channel-md-name");
|
|
2808
3021
|
nameEl.value = ch ? ch.name : "";
|
|
2809
3022
|
nameEl.disabled = locked;
|
|
2810
|
-
document.getElementById("channel-md-del")
|
|
3023
|
+
const del = document.getElementById("channel-md-del");
|
|
3024
|
+
const branched = Boolean(ch && ch.parentId);
|
|
3025
|
+
del.hidden = locked;
|
|
3026
|
+
del.setAttribute("data-i18n", branched ? "branch.close" : "channel.delete");
|
|
3027
|
+
del.textContent = t(branched ? "branch.close" : "channel.delete");
|
|
2811
3028
|
channelMdDialog.showModal();
|
|
2812
3029
|
}
|
|
2813
3030
|
const botMemoryDialog = document.getElementById("bot-memory");
|
|
@@ -2997,7 +3214,8 @@
|
|
|
2997
3214
|
document.getElementById("channel-md-del").addEventListener("click", async () => {
|
|
2998
3215
|
const ch = currentChannel();
|
|
2999
3216
|
try {
|
|
3000
|
-
await
|
|
3217
|
+
if (ch && ch.parentId) await closeBranch(state.id, ch);
|
|
3218
|
+
else await deleteChannel(state.id, ch && ch.name);
|
|
3001
3219
|
channelMdDialog.close();
|
|
3002
3220
|
} catch (err) {
|
|
3003
3221
|
alert(err.message);
|
|
@@ -4385,11 +4603,13 @@
|
|
|
4385
4603
|
if (!pending) return;
|
|
4386
4604
|
const fromCands = (cands || []).map((bot) => bot.id).filter(Boolean);
|
|
4387
4605
|
const fromReply = replyAuthorId(pending.replyTo);
|
|
4388
|
-
const botIds =
|
|
4389
|
-
?
|
|
4606
|
+
const botIds = summoned.length
|
|
4607
|
+
? summoned
|
|
4390
4608
|
: fromReply
|
|
4391
4609
|
? [fromReply]
|
|
4392
|
-
:
|
|
4610
|
+
: fromCands.length
|
|
4611
|
+
? fromCands
|
|
4612
|
+
: botsFromSend(pending.raw) || [];
|
|
4393
4613
|
const assigneeId = botIds.length === 1 ? botIds[0] : "";
|
|
4394
4614
|
const ids = botIds;
|
|
4395
4615
|
const split = splitSendTargets(ids);
|
|
@@ -4432,6 +4652,7 @@
|
|
|
4432
4652
|
if (replyTo) payload.replyTo = replyTo;
|
|
4433
4653
|
if (attachments.length) payload.attachments = attachments;
|
|
4434
4654
|
if (assigneeId) payload.assigneeId = assigneeId;
|
|
4655
|
+
if (botIds && botIds.length) payload.mentions = botIds;
|
|
4435
4656
|
if (here) {
|
|
4436
4657
|
state.messages = state.messages.concat([
|
|
4437
4658
|
{
|
|
@@ -4481,7 +4702,7 @@
|
|
|
4481
4702
|
'"><span class="avatar sm" style="background:' +
|
|
4482
4703
|
hue(bot.handle) +
|
|
4483
4704
|
'">' +
|
|
4484
|
-
|
|
4705
|
+
avatarFace(bot) +
|
|
4485
4706
|
'</span><span class="assign-copy"><strong>' +
|
|
4486
4707
|
escapeHtml(bot.name) +
|
|
4487
4708
|
"</strong><span>@" +
|
|
@@ -4547,8 +4768,15 @@
|
|
|
4547
4768
|
openAssign(cands, { raw: raw, packed: packed, replyTo: replyTo });
|
|
4548
4769
|
return;
|
|
4549
4770
|
}
|
|
4550
|
-
const
|
|
4551
|
-
const
|
|
4771
|
+
const fromReply = replyAuthorId(replyTo);
|
|
4772
|
+
const botIds = summoned.length
|
|
4773
|
+
? summoned
|
|
4774
|
+
: fromReply
|
|
4775
|
+
? [fromReply]
|
|
4776
|
+
: botsFromSend(raw);
|
|
4777
|
+
const assigneeId = summoned.length === 1
|
|
4778
|
+
? summoned[0]
|
|
4779
|
+
: fromReply || (cands.length === 1 ? cands[0].id : "");
|
|
4552
4780
|
await flushSend(raw, packed, replyTo, botIds, assigneeId);
|
|
4553
4781
|
});
|
|
4554
4782
|
function asLibList(value) {
|
|
@@ -5165,6 +5393,7 @@
|
|
|
5165
5393
|
group: p.name || p.id,
|
|
5166
5394
|
ready: Boolean(p.ready),
|
|
5167
5395
|
kind: p.kind || "key",
|
|
5396
|
+
reasoning: m.reasoning || null,
|
|
5168
5397
|
});
|
|
5169
5398
|
}
|
|
5170
5399
|
}
|
|
@@ -5178,10 +5407,45 @@
|
|
|
5178
5407
|
return hit ? hit.name : ref.model;
|
|
5179
5408
|
}
|
|
5180
5409
|
function reasonLabel(value) {
|
|
5410
|
+
if (value === "none") return t("reason.none");
|
|
5181
5411
|
if (value === "minimal") return t("reason.min");
|
|
5182
5412
|
if (value === "low") return t("reason.low");
|
|
5413
|
+
if (value === "medium") return t("reason.mid");
|
|
5183
5414
|
if (value === "high") return t("reason.high");
|
|
5184
|
-
return t("reason.
|
|
5415
|
+
if (value === "xhigh") return t("reason.xhigh");
|
|
5416
|
+
if (value === "max") return t("reason.max");
|
|
5417
|
+
return value || t("reason.mid");
|
|
5418
|
+
}
|
|
5419
|
+
function activeReasoningSpec() {
|
|
5420
|
+
const ref = activeModelRef();
|
|
5421
|
+
if (!ref) return null;
|
|
5422
|
+
const hit = allModels().find(
|
|
5423
|
+
(row) => row.provider === ref.provider && row.model === ref.model,
|
|
5424
|
+
);
|
|
5425
|
+
return (hit && hit.reasoning) || null;
|
|
5426
|
+
}
|
|
5427
|
+
function effortChoices(spec) {
|
|
5428
|
+
const raw = spec && spec.supportedEfforts;
|
|
5429
|
+
if (!raw || !raw.length) return [];
|
|
5430
|
+
return spec.mandatory
|
|
5431
|
+
? raw.filter((key) => key !== "none")
|
|
5432
|
+
: raw.slice();
|
|
5433
|
+
}
|
|
5434
|
+
function clampEffort(want, spec, fast) {
|
|
5435
|
+
const choices = effortChoices(spec);
|
|
5436
|
+
if (!choices.length) return "";
|
|
5437
|
+
if (fast) {
|
|
5438
|
+
if (choices.indexOf("low") >= 0) return "low";
|
|
5439
|
+
if (choices.indexOf("minimal") >= 0) return "minimal";
|
|
5440
|
+
return choices[choices.length - 1];
|
|
5441
|
+
}
|
|
5442
|
+
if (want && choices.indexOf(want) >= 0) return want;
|
|
5443
|
+
if (spec.defaultEffort && choices.indexOf(spec.defaultEffort) >= 0) {
|
|
5444
|
+
return spec.defaultEffort;
|
|
5445
|
+
}
|
|
5446
|
+
if (choices.indexOf("high") >= 0) return "high";
|
|
5447
|
+
if (choices.indexOf("medium") >= 0) return "medium";
|
|
5448
|
+
return choices[0];
|
|
5185
5449
|
}
|
|
5186
5450
|
function speedLabel() {
|
|
5187
5451
|
return modelCfg.fast ? t("speed.fast") : t("speed.normal");
|
|
@@ -5283,25 +5547,44 @@
|
|
|
5283
5547
|
}
|
|
5284
5548
|
document.getElementById("model-results").innerHTML =
|
|
5285
5549
|
html || '<p class="model-empty">' + t("noModels") + "</p>";
|
|
5286
|
-
document.getElementById("model-reasoning").value = modelCfg.reasoning || "medium";
|
|
5287
5550
|
document.getElementById("model-fast").checked = Boolean(modelCfg.fast);
|
|
5288
|
-
const
|
|
5551
|
+
const spec = activeReasoningSpec();
|
|
5552
|
+
const choices = effortChoices(spec);
|
|
5553
|
+
const reasonVal = clampEffort(
|
|
5554
|
+
modelCfg.reasoning,
|
|
5555
|
+
spec,
|
|
5556
|
+
Boolean(modelCfg.fast),
|
|
5557
|
+
);
|
|
5558
|
+
const reasonSel = document.getElementById("model-reasoning");
|
|
5559
|
+
reasonSel.innerHTML = choices
|
|
5560
|
+
.map(
|
|
5561
|
+
(value) =>
|
|
5562
|
+
'<option value="' +
|
|
5563
|
+
value +
|
|
5564
|
+
'">' +
|
|
5565
|
+
reasonLabel(value) +
|
|
5566
|
+
"</option>",
|
|
5567
|
+
)
|
|
5568
|
+
.join("");
|
|
5569
|
+
if (reasonVal) reasonSel.value = reasonVal;
|
|
5570
|
+
const reasonRow = document.querySelector('.model-nav [data-sheet="reasoning"]');
|
|
5571
|
+
if (reasonRow) reasonRow.hidden = !choices.length;
|
|
5572
|
+
if (!choices.length) {
|
|
5573
|
+
const pane = document.getElementById("model-sheet-reasoning");
|
|
5574
|
+
if (pane && !pane.hidden) showModelSheet("models");
|
|
5575
|
+
}
|
|
5576
|
+
const reason = choices.length ? reasonLabel(reasonVal) : "";
|
|
5289
5577
|
const speed = speedLabel();
|
|
5290
5578
|
const name = displayName(current);
|
|
5291
5579
|
document.getElementById("model-btn-label").textContent = current
|
|
5292
|
-
? name + " " + reason
|
|
5580
|
+
? name + (reason ? " " + reason : "")
|
|
5293
5581
|
: name;
|
|
5294
5582
|
document.getElementById("model-row-model").textContent = name;
|
|
5295
|
-
document.getElementById("model-row-reason").textContent = reason;
|
|
5583
|
+
document.getElementById("model-row-reason").textContent = reason || "—";
|
|
5296
5584
|
document.getElementById("model-row-speed").textContent = speed;
|
|
5297
|
-
document.getElementById("model-reason-list").innerHTML =
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
["medium", t("reason.mid")],
|
|
5301
|
-
["high", t("reason.high")],
|
|
5302
|
-
]
|
|
5303
|
-
.map(([value, label]) => {
|
|
5304
|
-
const on = (modelCfg.reasoning || "medium") === value ? " on" : "";
|
|
5585
|
+
document.getElementById("model-reason-list").innerHTML = choices
|
|
5586
|
+
.map((value) => {
|
|
5587
|
+
const on = reasonVal === value ? " on" : "";
|
|
5305
5588
|
const tick = on ? '<span class="tick">✓</span>' : "";
|
|
5306
5589
|
return (
|
|
5307
5590
|
'<button type="button" class="model-choice' +
|
|
@@ -5309,7 +5592,7 @@
|
|
|
5309
5592
|
'" data-reason="' +
|
|
5310
5593
|
value +
|
|
5311
5594
|
'">' +
|
|
5312
|
-
|
|
5595
|
+
reasonLabel(value) +
|
|
5313
5596
|
tick +
|
|
5314
5597
|
"</button>"
|
|
5315
5598
|
);
|
|
@@ -5537,7 +5820,9 @@
|
|
|
5537
5820
|
? '<span class="traj-av" style="background:' +
|
|
5538
5821
|
hue(bot.handle) +
|
|
5539
5822
|
'">' +
|
|
5540
|
-
|
|
5823
|
+
(portraitUrl(bot)
|
|
5824
|
+
? '<img alt="" src="' + escapeHtml(portraitUrl(bot)) + '">'
|
|
5825
|
+
: escapeHtml(glyph)) +
|
|
5541
5826
|
"</span>"
|
|
5542
5827
|
: '<span class="traj-av you">' + escapeHtml(glyph) + "</span>";
|
|
5543
5828
|
return (
|