bothouse-dsh 0.1.47 → 0.1.48
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/client.js +22 -2
- package/collab/cloudweb.mjs +34 -1
- package/collab/desk/index.html +271 -31
- package/collab/dsh-member.mjs +22 -1
- package/collab/later.mjs +21 -0
- package/collab/page.html +36 -0
- package/collab/plugin.mjs +76 -3
- package/collab/store.mjs +8 -0
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// dsh-client-source-sha256:
|
|
1
|
+
// dsh-client-source-sha256: 4cdb6b6bd9c4f3a8ef008fced3e4c011501f74d835505b158ecc38bde39ca3a8
|
|
2
2
|
// dsh-style-sha256: f5d384a5b03e6c383f622d9f8cb3563fb1695e55146740801d238d6606619709
|
|
3
3
|
window.__ModuleLoader__.load({
|
|
4
4
|
id: "bothouse-dsh",
|
|
@@ -104,6 +104,24 @@ const INITIAL = {
|
|
|
104
104
|
const MAX_BACKOFF_MS = 3e4;
|
|
105
105
|
/** Messages requested per page, both for the first read and for `loadOlder`. */
|
|
106
106
|
const PAGE_SIZE = 100;
|
|
107
|
+
/** Browser storage key holding the channel the owner had open before a reload. */
|
|
108
|
+
const ROOM_KEY = "dsh-collab.room";
|
|
109
|
+
/** Remember the open channel so reopening the panel returns to it instead of the first one. */
|
|
110
|
+
const rememberRoom = (roomId) => {
|
|
111
|
+
try {
|
|
112
|
+
globalThis.localStorage?.setItem(ROOM_KEY, String(roomId));
|
|
113
|
+
} catch (error) {}
|
|
114
|
+
};
|
|
115
|
+
/** Channel id remembered by an earlier visit, or undefined when absent or unreadable. */
|
|
116
|
+
const rememberedRoom = () => {
|
|
117
|
+
try {
|
|
118
|
+
const raw = globalThis.localStorage?.getItem(ROOM_KEY);
|
|
119
|
+
const id = raw === null || raw === void 0 || raw === "" ? NaN : Number(raw);
|
|
120
|
+
return Number.isInteger(id) ? id : void 0;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
107
125
|
/** Messages requested per catch-up read after a reconnect. */
|
|
108
126
|
const CATCH_UP_LIMIT = 200;
|
|
109
127
|
/** Status-coded service failure so the panel can tell "not enabled" from "not signed in". */
|
|
@@ -319,7 +337,8 @@ function createChannelSource(transport) {
|
|
|
319
337
|
try {
|
|
320
338
|
const state = await request("collab/api/state", { signal: read.controller.signal });
|
|
321
339
|
if (!current()) return;
|
|
322
|
-
const
|
|
340
|
+
const remembered = previous.roomId === void 0 ? rememberedRoom() : void 0;
|
|
341
|
+
const roomId = state.rooms.some((r) => r.id === previous.roomId) ? previous.roomId : state.rooms.some((r) => r.id === remembered) ? remembered : state.rooms[0]?.id;
|
|
323
342
|
const rows = [];
|
|
324
343
|
let hasMoreOlder = false;
|
|
325
344
|
if (roomId !== void 0) {
|
|
@@ -391,6 +410,7 @@ function createChannelSource(transport) {
|
|
|
391
410
|
};
|
|
392
411
|
const selectRoom = async (roomId) => {
|
|
393
412
|
historyEpoch += 1;
|
|
413
|
+
rememberRoom(roomId);
|
|
394
414
|
if (roomId !== snapshot.roomId) earlyTasks.clear();
|
|
395
415
|
const shown = outbox.filter((entry) => entry.roomId === roomId);
|
|
396
416
|
set(roomId === snapshot.roomId ? {
|
package/collab/cloudweb.mjs
CHANGED
|
@@ -52,7 +52,9 @@ export const NOTICE_PREFIXES = ['📬', '✅', '↪', '⏳', '🔄', '⏹', '⏸
|
|
|
52
52
|
const FOOTER = /(?:^|\n+)\*[^*\n]*·[^*]*\*\s*$/s;
|
|
53
53
|
const BARE_MENTION = '(只 @ 了你,没写内容:看上面的频道消息判断要你做什么;看不出来就问)';
|
|
54
54
|
export const PROGRESS_PREFIX = '__cc_connect_progress_card_v1__:';
|
|
55
|
-
|
|
55
|
+
// cancelled (#913): a card whose turn was cut without an outcome — a restarted bridge, a
|
|
56
|
+
// cancelled DSH job. page.html styles it like any other card state.
|
|
57
|
+
const CARD_STATES = { running: '处理中', completed: '已完成', failed: '失败', cancelled: '已取消' };
|
|
56
58
|
const RESERVED_IDS = new Set(['user', 'system']);
|
|
57
59
|
const MAX_NAME = 64;
|
|
58
60
|
|
|
@@ -245,9 +247,40 @@ export class CloudWebGateway {
|
|
|
245
247
|
this.hub.dropDrafts('role', id);
|
|
246
248
|
// ...and its pending permission requests are gone with the old process.
|
|
247
249
|
this.approvals.expireWhere(p => p.meta?.gateway === this && p.meta.bot === id);
|
|
250
|
+
// ...and the progress cards it already posted would stay 「处理中」 forever: the old
|
|
251
|
+
// process held the only handle, and the turn it resumes posts a second card (#913).
|
|
252
|
+
this.closeStaleCards(id);
|
|
248
253
|
return bot;
|
|
249
254
|
}
|
|
250
255
|
|
|
256
|
+
/**
|
|
257
|
+
* #913: close the progress cards a bot left running. A restarted cc-connect keeps no
|
|
258
|
+
* message ids, so nothing will ever edit those rows again, while its resumed turn posts
|
|
259
|
+
* a fresh card; the room then shows two 「处理中」 and no way to tell which is alive.
|
|
260
|
+
* They are closed as 已取消 (page.html already styles that state) and keep their steps,
|
|
261
|
+
* plus a line saying the turn was cut, so the history stays readable.
|
|
262
|
+
*
|
|
263
|
+
* Every card row this member posted is looked at, not the last page of one room: the
|
|
264
|
+
* room may have carried a hundred messages since (a busy channel, a workshop), and a
|
|
265
|
+
* running card there must still be closed. Only this member's own rows are touched,
|
|
266
|
+
* and only rows still `running`: a card the resumed turn reopens by its own handle goes
|
|
267
|
+
* back to 处理中 on the next frame.
|
|
268
|
+
*/
|
|
269
|
+
closeStaleCards(memberId) {
|
|
270
|
+
for (const row of this.hub.store.authorCards('role', memberId)) {
|
|
271
|
+
let card;
|
|
272
|
+
try { card = JSON.parse(row.card); } catch { continue; }
|
|
273
|
+
if (card?.state !== 'running') continue;
|
|
274
|
+
const note = '⏹ 已中断(桥重启),这一步不会再更新';
|
|
275
|
+
const brief = typeof card.brief === 'string' && card.brief !== '' ? `${card.brief}\n${note}` : note;
|
|
276
|
+
// The stored title is already rendered ("CC · 处理中"): swap only its state label,
|
|
277
|
+
// so the card keeps its agent name.
|
|
278
|
+
const label = ` · ${CARD_STATES[card.state] ?? ''}`;
|
|
279
|
+
const agent = typeof card.title === 'string' && card.title.endsWith(label) ? card.title.slice(0, -label.length) : (card.title ?? 'Agent');
|
|
280
|
+
this.hub.edit(row.id, 'role', memberId, `${row.text}\n${note}`, JSON.stringify({ ...card, state: 'cancelled', title: `${agent} · ${CARD_STATES.cancelled}`, brief }));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
251
284
|
/** Forward one new channel message to every connected bot except its author. */
|
|
252
285
|
onNew(msg) {
|
|
253
286
|
if (msg.author_kind === 'system' || msg.card !== null || this.bots.size === 0 || this.hub.teamExit?.answerFor(msg)) return;
|