@sparkelf/dsh-group-chat 0.5.2 → 0.5.4
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 +310 -179
- package/lib/client.js.map +1 -1
- package/lib/index.js +84 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -492,6 +492,29 @@ const messageId = (event) => {
|
|
|
492
492
|
* message text. Parsing text would misread a code sample containing `@`, and it
|
|
493
493
|
* would let any message smuggle itself into an execution path.
|
|
494
494
|
*/
|
|
495
|
+
/**
|
|
496
|
+
* Member ids a prompt's sender declared on its source, in pick order.
|
|
497
|
+
*
|
|
498
|
+
* The composer's roster source inserts chips with `appearance: 'mention'`, and the reference
|
|
499
|
+
* pipeline hands those ids to the Host on the prompt; this reads them back. Only ids that source
|
|
500
|
+
* owns qualify — another source's chip is a file, a session, or a quoted message, never a member.
|
|
501
|
+
*/
|
|
502
|
+
const declaredMentions = (source) => {
|
|
503
|
+
const raw = source?.mentions;
|
|
504
|
+
if (!Array.isArray(raw)) return [];
|
|
505
|
+
const out = [];
|
|
506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
507
|
+
for (const entry of raw) {
|
|
508
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
509
|
+
const record = entry;
|
|
510
|
+
if (record.sourceId !== "@sparkelf/dsh-group-chat/member") continue;
|
|
511
|
+
const memberId = record.itemId;
|
|
512
|
+
if (typeof memberId !== "string" || memberId.length === 0 || seen.has(memberId)) continue;
|
|
513
|
+
seen.add(memberId);
|
|
514
|
+
out.push(memberId);
|
|
515
|
+
}
|
|
516
|
+
return out;
|
|
517
|
+
};
|
|
495
518
|
const mentionsOf = (event) => {
|
|
496
519
|
const raw = eventSource(event)?.["mentions"];
|
|
497
520
|
if (!Array.isArray(raw)) return [];
|
|
@@ -769,6 +792,49 @@ const cleanupRoomSessions = async (input) => {
|
|
|
769
792
|
};
|
|
770
793
|
};
|
|
771
794
|
/**
|
|
795
|
+
* Register a room's Session with the workspace that owns its directory.
|
|
796
|
+
*
|
|
797
|
+
* The shell's `openSession` selects a Session that belongs to a Workspace, so a room created
|
|
798
|
+
* with a `cwd` and no workspace id is not selectable until this runs. Registration is idempotent
|
|
799
|
+
* on the registry (`attachSession` checks membership before writing), which is what lets the
|
|
800
|
+
* discovery poll run it on every boot without duplicating rows.
|
|
801
|
+
*
|
|
802
|
+
* Never throws: a room that cannot be registered still reads and writes through its Host, only
|
|
803
|
+
* the main view cannot follow it, and that is a state worth reporting rather than a failed open.
|
|
804
|
+
* @param registry - the workspace registry surface, absent on a minimal host.
|
|
805
|
+
* @param directory - the room directory the workspace is resolved by.
|
|
806
|
+
* @param sessionId - the room's canonical Session.
|
|
807
|
+
* @param log - optional warning sink; the plugin passes its Cordis logger.
|
|
808
|
+
* @returns whether the Session is now attached.
|
|
809
|
+
*/
|
|
810
|
+
const attachRoomSession = async (registry, directory, sessionId, log) => {
|
|
811
|
+
if (registry === void 0) {
|
|
812
|
+
log?.warn?.("[group-chat] the workspace registry is unavailable; the room session stays unlisted", sessionId);
|
|
813
|
+
return {
|
|
814
|
+
attached: false,
|
|
815
|
+
reason: "registry-unavailable"
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
try {
|
|
819
|
+
const workspace = await registry.resolveByPath(directory);
|
|
820
|
+
if (workspace === void 0) {
|
|
821
|
+
log?.warn?.("[group-chat] no workspace owns the room directory; the room session stays unlisted", sessionId, directory);
|
|
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
|
+
}
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
772
838
|
* Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
|
|
773
839
|
*
|
|
774
840
|
* This can be used with JS designed for browsers to improve reuse of code and
|
|
@@ -9248,6 +9314,7 @@ var RoomSessions = class {
|
|
|
9248
9314
|
const resumed = await this.resolvePersisted(sessionId, directory);
|
|
9249
9315
|
if (resumed !== void 0) {
|
|
9250
9316
|
await this.installRoomModel(resumed, room, false);
|
|
9317
|
+
await this.attachToWorkspace(sessionId, directory);
|
|
9251
9318
|
return resumed;
|
|
9252
9319
|
}
|
|
9253
9320
|
const created = await this.ctx.sessionController.create({
|
|
@@ -9262,6 +9329,7 @@ var RoomSessions = class {
|
|
|
9262
9329
|
const session = this.existing(roomId);
|
|
9263
9330
|
if (session === void 0) throw new Error(`group-chat could not open a session for room ${roomId}`);
|
|
9264
9331
|
await this.installRoomModel(session, room, true);
|
|
9332
|
+
await this.attachToWorkspace(sessionId, directory);
|
|
9265
9333
|
return session;
|
|
9266
9334
|
}
|
|
9267
9335
|
/** Load a persisted room Session, or `undefined` only when the controller denies it exists. */
|
|
@@ -9308,6 +9376,20 @@ var RoomSessions = class {
|
|
|
9308
9376
|
get archiveRegistry() {
|
|
9309
9377
|
return hostService(this.ctx, "workspaceRegistry");
|
|
9310
9378
|
}
|
|
9379
|
+
/**
|
|
9380
|
+
* Attach the room's Session to the workspace that owns its directory.
|
|
9381
|
+
*
|
|
9382
|
+
* The shell's `openSession` selects a Session that belongs to a Workspace, so a room created
|
|
9383
|
+
* with a `cwd` alone is not selectable and clicking it moves the highlight only. Attaching is
|
|
9384
|
+
* idempotent on the registry side, and every failure here is logged rather than fatal: a room
|
|
9385
|
+
* whose registration failed still reads and writes through its Host, only the main view
|
|
9386
|
+
* cannot follow it.
|
|
9387
|
+
* @param sessionId - the room's canonical Session.
|
|
9388
|
+
* @param directory - the room directory the workspace is resolved by.
|
|
9389
|
+
*/
|
|
9390
|
+
async attachToWorkspace(sessionId, directory) {
|
|
9391
|
+
await attachRoomSession(hostService(this.ctx, "workspaceRegistry"), directory, sessionId, this.ctx.logger);
|
|
9392
|
+
}
|
|
9311
9393
|
/** Hide a replacement the controller minted instead of honoring the room id. */
|
|
9312
9394
|
async archiveReplacement(sessionId) {
|
|
9313
9395
|
try {
|
|
@@ -9755,7 +9837,8 @@ const activate = (ctx, target) => {
|
|
|
9755
9837
|
if (typeof messageId === "string" && messageId.startsWith(WARM_UP_PREFIX)) return;
|
|
9756
9838
|
if (data.source?.kind === QUOTE_CONTEXT_KIND) return;
|
|
9757
9839
|
const content = data.content;
|
|
9758
|
-
const
|
|
9840
|
+
const declared = declaredMentions(data.source);
|
|
9841
|
+
const mentions = declared.length > 0 ? declared : mentionedMemberIds(textOfContent(content), runtimes.get(roomId)?.members ?? []);
|
|
9759
9842
|
const quote = pendingQuotes.get(roomId);
|
|
9760
9843
|
const outgoing = quote === void 0 ? content : withQuote(content, quote);
|
|
9761
9844
|
backend.sendMessage(roomId, {
|
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.4",
|
|
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",
|