@sparkelf/dsh-group-chat 0.5.4 → 0.5.6
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 +184 -171
- package/lib/client.js.map +1 -1
- package/lib/index.js +70 -50
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -791,48 +791,35 @@ const cleanupRoomSessions = async (input) => {
|
|
|
791
791
|
errors
|
|
792
792
|
};
|
|
793
793
|
};
|
|
794
|
+
//#endregion
|
|
795
|
+
//#region src/room-workspace.ts
|
|
794
796
|
/**
|
|
795
|
-
*
|
|
797
|
+
* Give a room's Session the workspace that makes it selectable.
|
|
796
798
|
*
|
|
797
|
-
* The
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
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.
|
|
801
803
|
*
|
|
802
|
-
*
|
|
803
|
-
*
|
|
804
|
-
* @param
|
|
805
|
-
* @param directory - the room directory the workspace is resolved by.
|
|
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.
|
|
806
807
|
* @param sessionId - the room's canonical Session.
|
|
807
|
-
* @
|
|
808
|
-
* @
|
|
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.
|
|
809
810
|
*/
|
|
810
|
-
const
|
|
811
|
-
if (registry === void 0) {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return {
|
|
823
|
-
attached: false,
|
|
824
|
-
reason: "workspace-not-found"
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
await workspace.attachSession(sessionId);
|
|
828
|
-
return { attached: true };
|
|
829
|
-
} catch (error) {
|
|
830
|
-
log?.warn?.("[group-chat] attaching the room session to its workspace failed", sessionId, error);
|
|
831
|
-
return {
|
|
832
|
-
attached: false,
|
|
833
|
-
reason: "attach-failed"
|
|
834
|
-
};
|
|
835
|
-
}
|
|
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
|
+
};
|
|
836
823
|
};
|
|
837
824
|
/**
|
|
838
825
|
* Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
|
|
@@ -9298,7 +9285,10 @@ var RoomSessions = class {
|
|
|
9298
9285
|
*/
|
|
9299
9286
|
async ensure(roomId, room) {
|
|
9300
9287
|
const existing = this.existing(roomId);
|
|
9301
|
-
if (existing !== void 0)
|
|
9288
|
+
if (existing !== void 0) {
|
|
9289
|
+
this.titleRoomSession(existing, room.name);
|
|
9290
|
+
return existing;
|
|
9291
|
+
}
|
|
9302
9292
|
const pending = this.pending.get(roomId);
|
|
9303
9293
|
if (pending !== void 0) return pending;
|
|
9304
9294
|
const opening = this.open(roomId, room).finally(() => {
|
|
@@ -9314,7 +9304,6 @@ var RoomSessions = class {
|
|
|
9314
9304
|
const resumed = await this.resolvePersisted(sessionId, directory);
|
|
9315
9305
|
if (resumed !== void 0) {
|
|
9316
9306
|
await this.installRoomModel(resumed, room, false);
|
|
9317
|
-
await this.attachToWorkspace(sessionId, directory);
|
|
9318
9307
|
return resumed;
|
|
9319
9308
|
}
|
|
9320
9309
|
const created = await this.ctx.sessionController.create({
|
|
@@ -9329,7 +9318,7 @@ var RoomSessions = class {
|
|
|
9329
9318
|
const session = this.existing(roomId);
|
|
9330
9319
|
if (session === void 0) throw new Error(`group-chat could not open a session for room ${roomId}`);
|
|
9331
9320
|
await this.installRoomModel(session, room, true);
|
|
9332
|
-
|
|
9321
|
+
this.titleRoomSession(session, room.name);
|
|
9333
9322
|
return session;
|
|
9334
9323
|
}
|
|
9335
9324
|
/** Load a persisted room Session, or `undefined` only when the controller denies it exists. */
|
|
@@ -9377,18 +9366,22 @@ var RoomSessions = class {
|
|
|
9377
9366
|
return hostService(this.ctx, "workspaceRegistry");
|
|
9378
9367
|
}
|
|
9379
9368
|
/**
|
|
9380
|
-
*
|
|
9369
|
+
* Give the room's Session the title a reader expects, once.
|
|
9381
9370
|
*
|
|
9382
|
-
*
|
|
9383
|
-
*
|
|
9384
|
-
*
|
|
9385
|
-
*
|
|
9386
|
-
*
|
|
9387
|
-
* @param sessionId - the room's canonical Session.
|
|
9388
|
-
* @param directory - the room directory the workspace is resolved by.
|
|
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>`.
|
|
9389
9376
|
*/
|
|
9390
|
-
|
|
9391
|
-
|
|
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
|
+
}
|
|
9392
9385
|
}
|
|
9393
9386
|
/** Hide a replacement the controller minted instead of honoring the room id. */
|
|
9394
9387
|
async archiveReplacement(sessionId) {
|
|
@@ -10063,6 +10056,33 @@ const handleRoomRequest = async (route, httpMethod, payload, url, backend, runti
|
|
|
10063
10056
|
* `purge: true` plus `confirm` equal to the room id. The canonical room Session is never a
|
|
10064
10057
|
* candidate, and neither is another room's Session or a subagent child.
|
|
10065
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
|
+
}
|
|
10066
10086
|
const cleanupMatch = /^rooms\/([^/]+)\/sessions\/cleanup$/u.exec(route);
|
|
10067
10087
|
if (cleanupMatch !== null && cleanupMatch[1] !== void 0) {
|
|
10068
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.6",
|
|
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",
|