@sparkelf/dsh-group-chat 0.5.3 → 0.5.5
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/lib/client.js +174 -142
- package/lib/client.js.map +1 -1
- package/lib/index.js +80 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -791,6 +791,36 @@ const cleanupRoomSessions = async (input) => {
|
|
|
791
791
|
errors
|
|
792
792
|
};
|
|
793
793
|
};
|
|
794
|
+
//#endregion
|
|
795
|
+
//#region src/room-workspace.ts
|
|
796
|
+
/**
|
|
797
|
+
* Give a room's Session the workspace that makes it selectable.
|
|
798
|
+
*
|
|
799
|
+
* The path is the Session's own `cwd`: that is the one path `attachSession` will accept, and it
|
|
800
|
+
* is stable for the life of the Session (a room's directory is fixed when the Session is
|
|
801
|
+
* created). Reuse is by exact path, so opening the same room twice never creates a second
|
|
802
|
+
* workspace.
|
|
803
|
+
*
|
|
804
|
+
* @param registry - the workspace registry, absent on a minimal host.
|
|
805
|
+
* @param path - the room Session's directory.
|
|
806
|
+
* @param title - display title for a newly created workspace.
|
|
807
|
+
* @param sessionId - the room's canonical Session.
|
|
808
|
+
* @returns which workspace the room belongs to, and whether this call created it.
|
|
809
|
+
* @throws when the registry refuses the create or the attach; the caller decides how to report.
|
|
810
|
+
*/
|
|
811
|
+
const ensureRoomWorkspace = async (registry, path, title, sessionId) => {
|
|
812
|
+
if (registry === void 0) return {
|
|
813
|
+
workspaceId: void 0,
|
|
814
|
+
created: false
|
|
815
|
+
};
|
|
816
|
+
const existing = await registry.resolveByPath(path);
|
|
817
|
+
const workspace = existing ?? await registry.create(path, title);
|
|
818
|
+
await workspace.attachSession(sessionId);
|
|
819
|
+
return {
|
|
820
|
+
workspaceId: workspace.id,
|
|
821
|
+
created: existing === void 0
|
|
822
|
+
};
|
|
823
|
+
};
|
|
794
824
|
/**
|
|
795
825
|
* Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
|
|
796
826
|
*
|
|
@@ -9255,7 +9285,10 @@ var RoomSessions = class {
|
|
|
9255
9285
|
*/
|
|
9256
9286
|
async ensure(roomId, room) {
|
|
9257
9287
|
const existing = this.existing(roomId);
|
|
9258
|
-
if (existing !== void 0)
|
|
9288
|
+
if (existing !== void 0) {
|
|
9289
|
+
this.titleRoomSession(existing, room.name);
|
|
9290
|
+
return existing;
|
|
9291
|
+
}
|
|
9259
9292
|
const pending = this.pending.get(roomId);
|
|
9260
9293
|
if (pending !== void 0) return pending;
|
|
9261
9294
|
const opening = this.open(roomId, room).finally(() => {
|
|
@@ -9285,6 +9318,7 @@ var RoomSessions = class {
|
|
|
9285
9318
|
const session = this.existing(roomId);
|
|
9286
9319
|
if (session === void 0) throw new Error(`group-chat could not open a session for room ${roomId}`);
|
|
9287
9320
|
await this.installRoomModel(session, room, true);
|
|
9321
|
+
this.titleRoomSession(session, room.name);
|
|
9288
9322
|
return session;
|
|
9289
9323
|
}
|
|
9290
9324
|
/** Load a persisted room Session, or `undefined` only when the controller denies it exists. */
|
|
@@ -9331,6 +9365,24 @@ var RoomSessions = class {
|
|
|
9331
9365
|
get archiveRegistry() {
|
|
9332
9366
|
return hostService(this.ctx, "workspaceRegistry");
|
|
9333
9367
|
}
|
|
9368
|
+
/**
|
|
9369
|
+
* Give the room's Session the title a reader expects, once.
|
|
9370
|
+
*
|
|
9371
|
+
* A room Session starts titled from its first message, which reads like an ordinary
|
|
9372
|
+
* conversation in the sidebar. Renaming happens only while the Session has never carried a
|
|
9373
|
+
* `session/title` event: once the plugin or the reader has named it, that choice stands.
|
|
9374
|
+
* @param session - the room's Session.
|
|
9375
|
+
* @param name - the room name, used as `群聊·<name>`.
|
|
9376
|
+
*/
|
|
9377
|
+
titleRoomSession(session, name) {
|
|
9378
|
+
if (session.snapshotEvents().some((event) => event.type === "session/title")) return;
|
|
9379
|
+
const titles = hostService(this.ctx, "sessionTitle");
|
|
9380
|
+
try {
|
|
9381
|
+
titles?.rename(session, `群聊·${name}`);
|
|
9382
|
+
} catch (error) {
|
|
9383
|
+
this.ctx.logger?.warn?.("[group-chat] naming the room session failed", session.id, error);
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9334
9386
|
/** Hide a replacement the controller minted instead of honoring the room id. */
|
|
9335
9387
|
async archiveReplacement(sessionId) {
|
|
9336
9388
|
try {
|
|
@@ -10004,6 +10056,33 @@ const handleRoomRequest = async (route, httpMethod, payload, url, backend, runti
|
|
|
10004
10056
|
* `purge: true` plus `confirm` equal to the room id. The canonical room Session is never a
|
|
10005
10057
|
* candidate, and neither is another room's Session or a subagent child.
|
|
10006
10058
|
*/
|
|
10059
|
+
/**
|
|
10060
|
+
* Make one room the view: resolve its Session, give it the workspace that makes it selectable,
|
|
10061
|
+
* and answer with the Session id. The browser cannot do any of this itself -- it has no registry
|
|
10062
|
+
* and no workspace, and `openSession` refuses a Session no workspace owns.
|
|
10063
|
+
*/
|
|
10064
|
+
const openMatch = /^rooms\/([^/]+)\/open$/u.exec(route);
|
|
10065
|
+
if (openMatch !== null && openMatch[1] !== void 0) {
|
|
10066
|
+
if (httpMethod !== "POST") throw new RouteRefusal("opening a room accepts POST", 405);
|
|
10067
|
+
const targetRoomId = decodeURIComponent(openMatch[1]);
|
|
10068
|
+
const room = (await backend.listRooms()).find((candidate) => candidate.id === targetRoomId);
|
|
10069
|
+
if (room === void 0) throw new RouteRefusal("this account is not a member of that room", 404);
|
|
10070
|
+
const session = await new RoomSessions(ctx).ensure(targetRoomId, {
|
|
10071
|
+
name: room.name,
|
|
10072
|
+
...room.cwd === null ? {} : { cwd: room.cwd },
|
|
10073
|
+
agentPreset: room.agentPreset ?? void 0,
|
|
10074
|
+
modelProvider: room.modelProvider ?? void 0,
|
|
10075
|
+
model: room.model ?? void 0,
|
|
10076
|
+
reasoningEffort: room.reasoningEffort ?? void 0
|
|
10077
|
+
});
|
|
10078
|
+
const directory = session.header.cwd ?? resolveRoomDirectory(targetRoomId, room.cwd);
|
|
10079
|
+
const workspace = await ensureRoomWorkspace(hostService(ctx, "workspaceRegistry"), directory, `群聊·${room.name}`, session.id);
|
|
10080
|
+
return {
|
|
10081
|
+
sessionId: session.id,
|
|
10082
|
+
workspaceId: workspace.workspaceId,
|
|
10083
|
+
workspaceCreated: workspace.created
|
|
10084
|
+
};
|
|
10085
|
+
}
|
|
10007
10086
|
const cleanupMatch = /^rooms\/([^/]+)\/sessions\/cleanup$/u.exec(route);
|
|
10008
10087
|
if (cleanupMatch !== null && cleanupMatch[1] !== void 0) {
|
|
10009
10088
|
if (httpMethod !== "POST") throw new RouteRefusal("session cleanup accepts POST", 405);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sparkelf/dsh-group-chat",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "Human-agent group chat for DataOps-managed DSH workspaces: rooms broadcast to every member, and only a member's own @mention wakes that member's agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|