@things-factory/ai-assistant 10.1.207 → 10.1.210
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/components/assistant-chat.ts +238 -9
- package/client/components/assistant-dock.ts +9 -4
- package/client/components/assistant-invite.ts +138 -0
- package/client/components/assistant-session-manager.ts +53 -12
- package/client/components/proposal-request.test.ts +10 -0
- package/client/components/roster-identity.test.ts +2 -12
- package/client/components/roster-identity.ts +0 -6
- package/client/setup-assistant-dock.ts +1 -0
- package/client/utils/assistant-session-actions.ts +3 -1
- package/client/utils/assistant-session-admin.ts +9 -2
- package/client/utils/assistant-session-controller.ts +1 -1
- package/client/utils/assistant-typing.test.ts +33 -0
- package/client/utils/assistant-typing.ts +53 -0
- package/dist-client/components/assistant-chat.d.ts +28 -0
- package/dist-client/components/assistant-chat.js +259 -8
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/assistant-dock.js +12 -4
- package/dist-client/components/assistant-dock.js.map +1 -1
- package/dist-client/components/assistant-invite.d.ts +39 -0
- package/dist-client/components/assistant-invite.js +150 -0
- package/dist-client/components/assistant-invite.js.map +1 -0
- package/dist-client/components/assistant-session-manager.d.ts +5 -1
- package/dist-client/components/assistant-session-manager.js +40 -10
- package/dist-client/components/assistant-session-manager.js.map +1 -1
- package/dist-client/components/proposal-request.test.js +8 -0
- package/dist-client/components/proposal-request.test.js.map +1 -1
- package/dist-client/components/roster-identity.d.ts +0 -2
- package/dist-client/components/roster-identity.js +0 -7
- package/dist-client/components/roster-identity.js.map +1 -1
- package/dist-client/components/roster-identity.test.js +2 -13
- package/dist-client/components/roster-identity.test.js.map +1 -1
- package/dist-client/setup-assistant-dock.js +1 -0
- package/dist-client/setup-assistant-dock.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/assistant-session-actions.d.ts +1 -0
- package/dist-client/utils/assistant-session-actions.js +3 -1
- package/dist-client/utils/assistant-session-actions.js.map +1 -1
- package/dist-client/utils/assistant-session-admin.d.ts +5 -2
- package/dist-client/utils/assistant-session-admin.js +3 -0
- package/dist-client/utils/assistant-session-admin.js.map +1 -1
- package/dist-client/utils/assistant-session-controller.d.ts +1 -1
- package/dist-client/utils/assistant-session-controller.js.map +1 -1
- package/dist-client/utils/assistant-typing.d.ts +20 -0
- package/dist-client/utils/assistant-typing.js +48 -0
- package/dist-client/utils/assistant-typing.js.map +1 -0
- package/dist-client/utils/assistant-typing.test.d.ts +1 -0
- package/dist-client/utils/assistant-typing.test.js +30 -0
- package/dist-client/utils/assistant-typing.test.js.map +1 -0
- package/dist-server/service/assistant-chat-resolver.js +5 -12
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/assistant-mode/index.d.ts +1 -1
- package/dist-server/service/assistant-mode/index.js +4 -1
- package/dist-server/service/assistant-mode/index.js.map +1 -1
- package/dist-server/service/assistant-mode/mode-categories.js +9 -2
- package/dist-server/service/assistant-mode/mode-categories.js.map +1 -1
- package/dist-server/service/assistant-mode/screen-rules.d.ts +19 -0
- package/dist-server/service/assistant-mode/screen-rules.js +40 -6
- package/dist-server/service/assistant-mode/screen-rules.js.map +1 -1
- package/dist-server/service/chat-message/chat-message-room.js +3 -1
- package/dist-server/service/chat-message/chat-message-room.js.map +1 -1
- package/dist-server/service/chat-message/chat-typing-subscription.d.ts +10 -0
- package/dist-server/service/chat-message/chat-typing-subscription.js +68 -0
- package/dist-server/service/chat-message/chat-typing-subscription.js.map +1 -0
- package/dist-server/service/chat-message/chat-typing.d.ts +28 -0
- package/dist-server/service/chat-message/chat-typing.js +40 -0
- package/dist-server/service/chat-message/chat-typing.js.map +1 -0
- package/dist-server/service/chat-session/attachment-tool-result.d.ts +1 -0
- package/dist-server/service/chat-session/attachment-tool-result.js +6 -0
- package/dist-server/service/chat-session/attachment-tool-result.js.map +1 -1
- package/dist-server/service/chat-session/attachment-tools.js +2 -2
- package/dist-server/service/chat-session/attachment-tools.js.map +1 -1
- package/dist-server/service/chat-session/chat-session.d.ts +2 -1
- package/dist-server/service/chat-session/chat-session.js +1 -1
- package/dist-server/service/chat-session/chat-session.js.map +1 -1
- package/dist-server/service/chat-session/session-activity-broadcast.d.ts +12 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js +15 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js.map +1 -1
- package/dist-server/service/chat-session/session-activity-publish.d.ts +2 -0
- package/dist-server/service/chat-session/session-activity-publish.js +5 -2
- package/dist-server/service/chat-session/session-activity-publish.js.map +1 -1
- package/dist-server/service/chat-session/session-visibility.d.ts +17 -2
- package/dist-server/service/chat-session/session-visibility.js +28 -3
- package/dist-server/service/chat-session/session-visibility.js.map +1 -1
- package/dist-server/service/chat-session/session-write-gate.d.ts +1 -1
- package/dist-server/service/chat-session/session-write-gate.js +1 -1
- package/dist-server/service/chat-session/session-write-gate.js.map +1 -1
- package/dist-server/service/chat-session-participant/index.d.ts +1 -0
- package/dist-server/service/chat-session-participant/index.js +4 -1
- package/dist-server/service/chat-session-participant/index.js.map +1 -1
- package/dist-server/service/chat-session-participant/invite-notification.d.ts +16 -1
- package/dist-server/service/chat-session-participant/invite-notification.js +34 -5
- package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -1
- package/dist-server/service/chat-session-participant/mention-invite.d.ts +30 -4
- package/dist-server/service/chat-session-participant/mention-invite.js +78 -31
- package/dist-server/service/chat-session-participant/mention-invite.js.map +1 -1
- package/dist-server/service/chat-session-participant/read-roster.js +5 -2
- package/dist-server/service/chat-session-participant/read-roster.js.map +1 -1
- package/dist-server/service/chat-session-resolver.d.ts +27 -0
- package/dist-server/service/chat-session-resolver.js +226 -25
- package/dist-server/service/chat-session-resolver.js.map +1 -1
- package/dist-server/service/data-entry/entry-target.d.ts +19 -0
- package/dist-server/service/data-entry/entry-target.js +20 -5
- package/dist-server/service/data-entry/entry-target.js.map +1 -1
- package/dist-server/service/data-entry/entry-tools.js +2 -2
- package/dist-server/service/data-entry/entry-tools.js.map +1 -1
- package/dist-server/service/data-entry/entry-update.d.ts +2 -1
- package/dist-server/service/data-entry/entry-update.js +5 -3
- package/dist-server/service/data-entry/entry-update.js.map +1 -1
- package/dist-server/service/data-read/list-read.d.ts +10 -0
- package/dist-server/service/data-read/list-read.js +12 -0
- package/dist-server/service/data-read/list-read.js.map +1 -1
- package/dist-server/service/data-read/read-target.d.ts +7 -0
- package/dist-server/service/data-read/read-target.js +2 -2
- package/dist-server/service/data-read/read-target.js.map +1 -1
- package/dist-server/service/data-read/read-tools.js +1 -1
- package/dist-server/service/data-read/read-tools.js.map +1 -1
- package/dist-server/service/index.d.ts +4 -2
- package/dist-server/service/index.js +9 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/testing/conversation-script.d.ts +1 -1
- package/dist-server/service/testing/conversation-script.js.map +1 -1
- package/dist-server/service/testing/mode-conformance.js +4 -0
- package/dist-server/service/testing/mode-conformance.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/server/explicit-graphql-types.test.ts +1 -1
- package/server/service/assistant-chat-resolver.ts +7 -12
- package/server/service/assistant-mode/index.ts +1 -1
- package/server/service/assistant-mode/mode-categories.ts +11 -3
- package/server/service/assistant-mode/mode-front.test.ts +53 -1
- package/server/service/assistant-mode/resolve.test.ts +2 -1
- package/server/service/assistant-mode/screen-rules.test.ts +41 -3
- package/server/service/assistant-mode/screen-rules.ts +44 -5
- package/server/service/chat-message/chat-message-room.test.ts +7 -2
- package/server/service/chat-message/chat-message-room.ts +3 -1
- package/server/service/chat-message/chat-typing-subscription.ts +52 -0
- package/server/service/chat-message/chat-typing.test.ts +45 -0
- package/server/service/chat-message/chat-typing.ts +48 -0
- package/server/service/chat-message/fold-history-window.test.ts +1 -1
- package/server/service/chat-session/attachment-tool-result.test.ts +9 -1
- package/server/service/chat-session/attachment-tool-result.ts +7 -0
- package/server/service/chat-session/attachment-tools.ts +3 -3
- package/server/service/chat-session/chat-session.ts +3 -2
- package/server/service/chat-session/group-room.test.ts +233 -0
- package/server/service/chat-session/session-activity-broadcast.ts +20 -0
- package/server/service/chat-session/session-activity-publish.ts +6 -1
- package/server/service/chat-session/session-activity-receivers.test.ts +26 -0
- package/server/service/chat-session/session-visibility.test.ts +32 -1
- package/server/service/chat-session/session-visibility.ts +33 -4
- package/server/service/chat-session/session-write-gate.test.ts +7 -1
- package/server/service/chat-session/session-write-gate.ts +2 -2
- package/server/service/chat-session-participant/index.ts +2 -0
- package/server/service/chat-session-participant/invite-notification.test.ts +36 -1
- package/server/service/chat-session-participant/invite-notification.ts +43 -5
- package/server/service/chat-session-participant/mention-invite.test.ts +49 -1
- package/server/service/chat-session-participant/mention-invite.ts +82 -28
- package/server/service/chat-session-participant/read-roster.test.ts +13 -4
- package/server/service/chat-session-participant/read-roster.ts +6 -3
- package/server/service/chat-session-resolver.ts +204 -27
- package/server/service/data-entry/data-entry-update.test.ts +19 -0
- package/server/service/data-entry/data-entry.test.ts +65 -1
- package/server/service/data-entry/entry-target.ts +36 -5
- package/server/service/data-entry/entry-tools.ts +2 -2
- package/server/service/data-entry/entry-update.ts +6 -3
- package/server/service/data-read/list-missing.test.ts +49 -0
- package/server/service/data-read/list-read.ts +18 -0
- package/server/service/data-read/read-target.ts +10 -3
- package/server/service/data-read/read-tools.ts +1 -1
- package/server/service/data-read/record-keys.test.ts +36 -0
- package/server/service/index.ts +6 -1
- package/server/service/testing/conversation-script.ts +1 -1
- package/server/service/testing/mode-conformance.ts +2 -0
- package/test/assistant-dock.test.ts +5 -1
- package/test/assistant-session-manager.test.ts +4 -2
- package/test/open-doors.test.ts +10 -5
- package/translations/en.json +29 -4
- package/translations/es.json +29 -4
- package/translations/ja.json +29 -4
- package/translations/ko.json +30 -5
- package/translations/ms.json +29 -4
- package/translations/zh.json +29 -4
|
@@ -20,30 +20,28 @@ const session_inbox_js_1 = require("./chat-session/session-inbox.js");
|
|
|
20
20
|
const carried_turns_js_1 = require("./chat-message/carried-turns.js");
|
|
21
21
|
const room_message_js_1 = require("./chat-message/room-message.js");
|
|
22
22
|
const reader_gate_js_1 = require("./chat-message/reader-gate.js");
|
|
23
|
+
const request_translator_js_1 = require("./request-translator.js");
|
|
23
24
|
const session_visibility_js_1 = require("./chat-session/session-visibility.js");
|
|
24
25
|
const mention_invite_js_1 = require("./chat-session-participant/mention-invite.js");
|
|
26
|
+
const chat_typing_js_1 = require("./chat-message/chat-typing.js");
|
|
25
27
|
const invite_notification_js_1 = require("./chat-session-participant/invite-notification.js");
|
|
26
28
|
const read_roster_js_1 = require("./chat-session-participant/read-roster.js");
|
|
27
29
|
const participant_lookup_js_1 = require("./chat-session-participant/participant-lookup.js");
|
|
28
30
|
const session_write_gate_js_1 = require("./chat-session/session-write-gate.js");
|
|
31
|
+
/** 초대 알림 — 발송은 호스트의 기전을 그대로 쓴다(`sendInviteNotification`, 문 셋이 같은 한 곳). */
|
|
32
|
+
async function notifyInvited(context, invited, inviter, sessionName) {
|
|
33
|
+
await (0, invite_notification_js_1.sendInviteNotification)(context.state?.notify, invited, inviter, sessionName, undefined, (0, request_translator_js_1.requestTranslator)(context));
|
|
34
|
+
}
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* 알림이 안 가도 초대 자체는 이미 일어났다. 그래서 실패를 삼키되, 초대를 되돌리지는 않는다 —
|
|
33
|
-
* 참여자 목록이 정본이고 알림은 그것을 알리는 편지다.
|
|
36
|
+
* A private room is the owner's alone (ADR-0073 decision 3) - closing a room to private sends its members out.
|
|
37
|
+
* Returns who left, so their lists can be told once.
|
|
34
38
|
*/
|
|
35
|
-
async function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
await context.state?.notify?.(payload);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
/* 편지가 못 갔다고 방에 들어온 사실이 취소되지는 않는다. */
|
|
46
|
-
}
|
|
39
|
+
async function sendOutMembers(domainId, sessionId, tx) {
|
|
40
|
+
const repo = (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant, tx);
|
|
41
|
+
const seats = await repo.find({ where: { domain: { id: domainId }, session: { id: sessionId }, role: 'member' } });
|
|
42
|
+
for (const seat of seats)
|
|
43
|
+
await repo.softDelete(seat.id);
|
|
44
|
+
return seats.map(seat => seat.userId).filter(Boolean);
|
|
47
45
|
}
|
|
48
46
|
/** Every door that writes into a conversation passes this first (`session-write-gate.ts`). */
|
|
49
47
|
async function writeGate(session, context, tx) {
|
|
@@ -123,7 +121,7 @@ tslib_1.__decorate([
|
|
|
123
121
|
tslib_1.__metadata("design:type", Boolean)
|
|
124
122
|
], ChatSessionInboxEntry.prototype, "hidden", void 0);
|
|
125
123
|
tslib_1.__decorate([
|
|
126
|
-
(0, type_graphql_1.Field)({ description: "Who may read the session: 'open' (everyone in the domain) or 'private' (
|
|
124
|
+
(0, type_graphql_1.Field)({ description: "Who may read the session: 'open' (everyone in the domain), 'group' (the owner and the people brought in) or 'private' (the owner alone)." }),
|
|
127
125
|
tslib_1.__metadata("design:type", String)
|
|
128
126
|
], ChatSessionInboxEntry.prototype, "visibility", void 0);
|
|
129
127
|
tslib_1.__decorate([
|
|
@@ -181,8 +179,17 @@ function attachmentRefusalMessage(refusal, context) {
|
|
|
181
179
|
}
|
|
182
180
|
let ChatSessionResolver = class ChatSessionResolver {
|
|
183
181
|
async chatSession(id, context) {
|
|
184
|
-
const { domain } = context.state;
|
|
185
|
-
|
|
182
|
+
const { domain, user } = context.state;
|
|
183
|
+
const session = await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).findOneBy({ id, domain: { id: domain.id } });
|
|
184
|
+
if (!session)
|
|
185
|
+
return null;
|
|
186
|
+
/*
|
|
187
|
+
* The row carries the name, the last preview and the summary of older messages - the conversation in
|
|
188
|
+
* short. A room the caller cannot enter answers as a missing one (ADR-0073: every reading path passes the gate).
|
|
189
|
+
*/
|
|
190
|
+
if (!(0, session_visibility_js_1.isOpenRoom)(session.visibility) && !(0, session_visibility_js_1.mayReadSession)(session.visibility, await (0, participant_lookup_js_1.isSessionParticipant)(domain.id, id, user?.id)))
|
|
191
|
+
return null;
|
|
192
|
+
return session;
|
|
186
193
|
}
|
|
187
194
|
async chatSessionsByAnchor(anchorType, anchorId, context) {
|
|
188
195
|
const { domain } = context.state;
|
|
@@ -386,7 +393,54 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
386
393
|
return false;
|
|
387
394
|
if (session.visibility === visibility)
|
|
388
395
|
return true;
|
|
396
|
+
/*
|
|
397
|
+
* Private is the owner's alone (ADR-0073 decision 3). The members leave with the change - a private room
|
|
398
|
+
* with people still seated in it would be a group under another name. Bringing them back is inviting
|
|
399
|
+
* them again, in a group.
|
|
400
|
+
*/
|
|
401
|
+
const sentOut = visibility === 'private' ? await sendOutMembers(domain.id, sessionId) : [];
|
|
389
402
|
await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).update(sessionId, { visibility, updater: user });
|
|
403
|
+
(0, session_activity_publish_js_1.publishSessionActivity)({
|
|
404
|
+
domainId: domain.id,
|
|
405
|
+
sessionId,
|
|
406
|
+
kind: 'meta',
|
|
407
|
+
anchorType: session.anchorType,
|
|
408
|
+
anchorId: session.anchorId,
|
|
409
|
+
actorId: user?.id,
|
|
410
|
+
alsoTo: sentOut
|
|
411
|
+
});
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Bring people into this conversation - the same seat a mention gives, by the same rule (`maySeat`): anyone
|
|
416
|
+
* in an open room, the owner in a group, nobody in a private one. Each person seated is told
|
|
417
|
+
* (`notifyInvited`). People outside the domain are not seated.
|
|
418
|
+
*/
|
|
419
|
+
async inviteToAISession(sessionId, userIds, context) {
|
|
420
|
+
const { domain, user } = context.state;
|
|
421
|
+
const session = await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } });
|
|
422
|
+
if (!session)
|
|
423
|
+
throw new Error('chat session not found');
|
|
424
|
+
/* Only people of this domain - an id from elsewhere is not a colleague here. */
|
|
425
|
+
const wanted = [...new Set((userIds ?? []).filter(id => typeof id === 'string' && id))].slice(0, 50);
|
|
426
|
+
if (wanted.length === 0)
|
|
427
|
+
return [];
|
|
428
|
+
const members = await (0, get_domain_users_js_1.buildDomainUsersQueryBuilder)(domain.id, 'USER').andWhere('USER.id IN (:...ids)', { ids: wanted }).getMany();
|
|
429
|
+
const known = new Set(members.map(member => member.id));
|
|
430
|
+
const seat = await (0, mention_invite_js_1.seatParticipants)({
|
|
431
|
+
sessionId,
|
|
432
|
+
domainId: domain.id,
|
|
433
|
+
inviter: user,
|
|
434
|
+
userIds: wanted.filter(id => known.has(id)),
|
|
435
|
+
participants: (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant),
|
|
436
|
+
visibility: session.visibility
|
|
437
|
+
});
|
|
438
|
+
if (seat.notSeated?.length) {
|
|
439
|
+
throw new Error(session.visibility === 'private'
|
|
440
|
+
? (0, request_translator_js_1.requestTranslator)(context)('ai-assistant.text.invite-private-refused', { defaultValue: 'a private conversation is yours alone. make it a group to bring people in.' })
|
|
441
|
+
: (0, request_translator_js_1.requestTranslator)(context)('ai-assistant.text.invite-owner-only', { defaultValue: 'only the person who started this group conversation can bring people in.' }));
|
|
442
|
+
}
|
|
443
|
+
await notifyInvited(context, seat.invited, user, session.name);
|
|
390
444
|
(0, session_activity_publish_js_1.publishSessionActivity)({
|
|
391
445
|
domainId: domain.id,
|
|
392
446
|
sessionId,
|
|
@@ -395,6 +449,77 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
395
449
|
anchorId: session.anchorId,
|
|
396
450
|
actorId: user?.id
|
|
397
451
|
});
|
|
452
|
+
return seat.invited;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Send a member out - the owner only, and never the owner themself. In a group the seat was the key, so the
|
|
456
|
+
* person no longer reads or writes here (every gate asks the seat). Their list is told once and drops it.
|
|
457
|
+
*/
|
|
458
|
+
async removeFromAISession(sessionId, userId, context) {
|
|
459
|
+
const { domain, user } = context.state;
|
|
460
|
+
const repo = (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant);
|
|
461
|
+
const seats = await repo.find({ where: { domain: { id: domain.id }, session: { id: sessionId } } });
|
|
462
|
+
const mine = seats.find(seat => seat.userId === user?.id);
|
|
463
|
+
if (mine?.role !== 'owner') {
|
|
464
|
+
throw new Error((0, request_translator_js_1.requestTranslator)(context)('ai-assistant.text.remove-owner-only', { defaultValue: 'only the person who started this conversation can send someone out.' }));
|
|
465
|
+
}
|
|
466
|
+
const theirs = seats.find(seat => seat.userId === userId && seat.role === 'member');
|
|
467
|
+
if (!theirs)
|
|
468
|
+
return false;
|
|
469
|
+
return await this._leave(sessionId, theirs, context);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Leave this conversation - a member steps out of their own seat. The owner does not leave: the room would be
|
|
473
|
+
* nobody's. They delete it, or make it private.
|
|
474
|
+
*/
|
|
475
|
+
async leaveAISession(sessionId, context) {
|
|
476
|
+
const { domain, user } = context.state;
|
|
477
|
+
if (!user?.id)
|
|
478
|
+
return false;
|
|
479
|
+
const mine = await (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant).findOne({
|
|
480
|
+
where: { domain: { id: domain.id }, session: { id: sessionId }, user: { id: user.id } }
|
|
481
|
+
});
|
|
482
|
+
if (mine?.role === 'owner') {
|
|
483
|
+
throw new Error((0, request_translator_js_1.requestTranslator)(context)('ai-assistant.text.owner-cannot-leave', { defaultValue: 'you started this conversation, so you cannot leave it. delete it, or make it private.' }));
|
|
484
|
+
}
|
|
485
|
+
if (mine?.role !== 'member')
|
|
486
|
+
return false;
|
|
487
|
+
return await this._leave(sessionId, mine, context);
|
|
488
|
+
}
|
|
489
|
+
async _leave(sessionId, seat, context) {
|
|
490
|
+
const { domain, user } = context.state;
|
|
491
|
+
const session = await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } });
|
|
492
|
+
await (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant).softDelete(seat.id);
|
|
493
|
+
(0, session_activity_publish_js_1.publishSessionActivity)({
|
|
494
|
+
domainId: domain.id,
|
|
495
|
+
sessionId,
|
|
496
|
+
kind: 'meta',
|
|
497
|
+
anchorType: session?.anchorType,
|
|
498
|
+
anchorId: session?.anchorId,
|
|
499
|
+
actorId: user?.id,
|
|
500
|
+
alsoTo: [seat.userId].filter(Boolean)
|
|
501
|
+
});
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* "I am typing" - a notice to the others in the room, stored nowhere. Only a person who may write here is
|
|
506
|
+
* typing here (the write gate), and the listeners pass the room's read gate (`ChatTypingSubscription`).
|
|
507
|
+
*/
|
|
508
|
+
async sayTyping(sessionId, context) {
|
|
509
|
+
const { domain, user } = context.state;
|
|
510
|
+
const session = await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } });
|
|
511
|
+
if (!session)
|
|
512
|
+
return false;
|
|
513
|
+
await writeGate(session, context);
|
|
514
|
+
const notice = (0, chat_typing_js_1.typingNotice)({ domainId: domain.id, sessionId, user });
|
|
515
|
+
if (!notice)
|
|
516
|
+
return false;
|
|
517
|
+
try {
|
|
518
|
+
shell_1.pubsub.publish(chat_typing_js_1.CHAT_TYPING_TOPIC, notice);
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
/* a missed notice is a line that does not show - nothing else rests on it */
|
|
522
|
+
}
|
|
398
523
|
return true;
|
|
399
524
|
}
|
|
400
525
|
async renameAISession(sessionId, name, context) {
|
|
@@ -460,6 +585,20 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
460
585
|
find: options => (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant).find(options)
|
|
461
586
|
});
|
|
462
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* May the caller seat someone here - the server's answer to the screen, so the screen keeps no copy of the
|
|
590
|
+
* rule (`maySeat`). The invite button and the line under a mention read it; the doors decide again.
|
|
591
|
+
*/
|
|
592
|
+
async mayInviteToAISession(sessionId, context) {
|
|
593
|
+
const { domain, user } = context.state;
|
|
594
|
+
const session = await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } });
|
|
595
|
+
if (!session || !user?.id)
|
|
596
|
+
return false;
|
|
597
|
+
const seat = await (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant).findOne({
|
|
598
|
+
where: { domain: { id: domain.id }, session: { id: sessionId }, user: { id: user.id } }
|
|
599
|
+
});
|
|
600
|
+
return (0, session_visibility_js_1.maySeat)(session.visibility, seat?.role);
|
|
601
|
+
}
|
|
463
602
|
async touchPresence(sessionId, context) {
|
|
464
603
|
const { domain, user } = context.state;
|
|
465
604
|
const repo = (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant);
|
|
@@ -483,7 +622,7 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
483
622
|
* 공개 대화는 도메인 안이면 누구나 읽는다 — 초대 절차가 없다(ADR-0073 판정 2). 비공개 대화는
|
|
484
623
|
* 참여자만. 여기서 막히는 것은 **방**이고, 방에 들어온 뒤 값을 읽는 일은 아래 문 판정이 따로 본다.
|
|
485
624
|
*/
|
|
486
|
-
if (session.visibility
|
|
625
|
+
if (!(0, session_visibility_js_1.isOpenRoom)(session.visibility)) {
|
|
487
626
|
/* A hide-only `viewer` row is not being in the room - it must not open a private conversation. */
|
|
488
627
|
const part = await (0, participant_lookup_js_1.isSessionParticipant)(domain.id, sessionId, user?.id);
|
|
489
628
|
if (!(0, session_visibility_js_1.mayReadSession)(session.visibility, part))
|
|
@@ -678,9 +817,9 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
678
817
|
});
|
|
679
818
|
/*
|
|
680
819
|
* 멘션은 사람을 불러들인다 — **사람의 말을 저장하는 문**만 이것을 한다(ADR-0071 결정 1 ①).
|
|
681
|
-
*
|
|
820
|
+
* 누가 자리를 주는지는 `maySeat` 가 정한다. 공개 방에서는 말한 사람도 앉는다(`seatTurn`).
|
|
682
821
|
*/
|
|
683
|
-
const invite = await (0, mention_invite_js_1.
|
|
822
|
+
const invite = await (0, mention_invite_js_1.seatTurn)({
|
|
684
823
|
content: body,
|
|
685
824
|
sessionId: session.id,
|
|
686
825
|
domainId: domain.id,
|
|
@@ -689,7 +828,7 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
689
828
|
/* 비공개 대화에서는 이름만 닿는다 — ADR-0073 판정 2. */
|
|
690
829
|
visibility: session.visibility
|
|
691
830
|
});
|
|
692
|
-
await notifyInvited(context, invite.invited, user);
|
|
831
|
+
await notifyInvited(context, invite.invited, user, session.name);
|
|
693
832
|
/* 사람의 말이다 — 목록의 최근 활동과 다른 참여자의 안 읽음이 이것으로 움직인다. */
|
|
694
833
|
try {
|
|
695
834
|
await (0, shell_1.getRepository)(chat_session_js_1.ChatSession, tx).update(session.id, (0, session_inbox_js_1.activityPatch)(body));
|
|
@@ -809,7 +948,12 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
809
948
|
});
|
|
810
949
|
saved.push(previous);
|
|
811
950
|
}
|
|
812
|
-
|
|
951
|
+
/*
|
|
952
|
+
* Carried words do not land in front of the whole domain. An open room closes to its owner; a group stays
|
|
953
|
+
* a group - its people were chosen by the owner, and carrying into it is writing to them.
|
|
954
|
+
*/
|
|
955
|
+
if (carried && (0, session_visibility_js_1.isOpenRoom)(session.visibility)) {
|
|
956
|
+
await sendOutMembers(domain.id, session.id, tx);
|
|
813
957
|
await (0, shell_1.getRepository)(chat_session_js_1.ChatSession, tx).update(session.id, { visibility: 'private', updater: user });
|
|
814
958
|
}
|
|
815
959
|
/* 활동은 마지막 발언 기준 한 번만 — 옮긴 줄마다 목록을 흔들 이유가 없다. */
|
|
@@ -934,7 +1078,7 @@ tslib_1.__decorate([
|
|
|
934
1078
|
], ChatSessionResolver.prototype, "hideAISession", null);
|
|
935
1079
|
tslib_1.__decorate([
|
|
936
1080
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
937
|
-
description: "
|
|
1081
|
+
description: "Set who reads this conversation: 'open' (everyone in the domain), 'group' (the owner and the people the owner brings in) or 'private' (the owner alone - the members are sent out). Only the owner may change it. Opening exposes what is already written — the screen says so before asking. Door checks on assistant answers are unaffected (ADR-0073)."
|
|
938
1082
|
}),
|
|
939
1083
|
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
940
1084
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
@@ -944,6 +1088,52 @@ tslib_1.__decorate([
|
|
|
944
1088
|
tslib_1.__metadata("design:paramtypes", [String, String, Object]),
|
|
945
1089
|
tslib_1.__metadata("design:returntype", Promise)
|
|
946
1090
|
], ChatSessionResolver.prototype, "setSessionVisibility", null);
|
|
1091
|
+
tslib_1.__decorate([
|
|
1092
|
+
(0, type_graphql_1.Mutation)(() => [String], {
|
|
1093
|
+
description: "Bring people into this conversation and tell them. In a group only the owner may; a private conversation takes nobody (make it a group first). Returns the ids of the people seated now - people already in it are left out."
|
|
1094
|
+
}),
|
|
1095
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
1096
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
1097
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('userIds', () => [String])),
|
|
1098
|
+
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
1099
|
+
tslib_1.__metadata("design:type", Function),
|
|
1100
|
+
tslib_1.__metadata("design:paramtypes", [String, Array, Object]),
|
|
1101
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
1102
|
+
], ChatSessionResolver.prototype, "inviteToAISession", null);
|
|
1103
|
+
tslib_1.__decorate([
|
|
1104
|
+
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
1105
|
+
description: 'Send a member out of this conversation. Only the owner may, and the owner cannot be sent out. In a group the person no longer reads or writes here.'
|
|
1106
|
+
}),
|
|
1107
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
1108
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
1109
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('userId')),
|
|
1110
|
+
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
1111
|
+
tslib_1.__metadata("design:type", Function),
|
|
1112
|
+
tslib_1.__metadata("design:paramtypes", [String, String, Object]),
|
|
1113
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
1114
|
+
], ChatSessionResolver.prototype, "removeFromAISession", null);
|
|
1115
|
+
tslib_1.__decorate([
|
|
1116
|
+
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
1117
|
+
description: 'Leave this conversation. A member may; the owner cannot (delete it or make it private instead). In a group, leaving ends reading and writing here.'
|
|
1118
|
+
}),
|
|
1119
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
1120
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
1121
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
1122
|
+
tslib_1.__metadata("design:type", Function),
|
|
1123
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
1124
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
1125
|
+
], ChatSessionResolver.prototype, "leaveAISession", null);
|
|
1126
|
+
tslib_1.__decorate([
|
|
1127
|
+
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
1128
|
+
description: 'Tell the others in this conversation that the caller is typing. Nothing is stored; send it every few seconds while typing. Only a person who may write here may send it.'
|
|
1129
|
+
}),
|
|
1130
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
1131
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
1132
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
1133
|
+
tslib_1.__metadata("design:type", Function),
|
|
1134
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
1135
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
1136
|
+
], ChatSessionResolver.prototype, "sayTyping", null);
|
|
947
1137
|
tslib_1.__decorate([
|
|
948
1138
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
949
1139
|
description: 'Rename a chat session. Any participant may rename it — the name is the topic label of a shared deliberation and the change is reversible. Broadcast as meta activity so every participant sees it without raising unread badges.'
|
|
@@ -990,6 +1180,17 @@ tslib_1.__decorate([
|
|
|
990
1180
|
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
991
1181
|
tslib_1.__metadata("design:returntype", Promise)
|
|
992
1182
|
], ChatSessionResolver.prototype, "chatSessionParticipants", null);
|
|
1183
|
+
tslib_1.__decorate([
|
|
1184
|
+
(0, type_graphql_1.Query)(() => Boolean, {
|
|
1185
|
+
description: "Whether the caller may bring people into this conversation now - anyone in an open one, the owner of a group, nobody in a private one. A hint for the screen; the invite and the mention decide again."
|
|
1186
|
+
}),
|
|
1187
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
1188
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
1189
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
1190
|
+
tslib_1.__metadata("design:type", Function),
|
|
1191
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
1192
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
1193
|
+
], ChatSessionResolver.prototype, "mayInviteToAISession", null);
|
|
993
1194
|
tslib_1.__decorate([
|
|
994
1195
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
995
1196
|
description: 'Presence heartbeat — bump current user\'s lastSeenAt in the session (online indicator).'
|