@things-factory/ai-assistant 10.1.214 → 10.1.218

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.
Files changed (69) hide show
  1. package/client/components/assistant-dock.ts +6 -3
  2. package/client/components/assistant-session-manager.ts +263 -12
  3. package/client/utils/assistant-session-actions.ts +12 -0
  4. package/client/utils/assistant-session-admin.ts +70 -5
  5. package/dist-client/components/assistant-dock.js +6 -3
  6. package/dist-client/components/assistant-dock.js.map +1 -1
  7. package/dist-client/components/assistant-session-manager.d.ts +37 -0
  8. package/dist-client/components/assistant-session-manager.js +281 -13
  9. package/dist-client/components/assistant-session-manager.js.map +1 -1
  10. package/dist-client/tsconfig.tsbuildinfo +1 -1
  11. package/dist-client/utils/assistant-session-actions.d.ts +15 -0
  12. package/dist-client/utils/assistant-session-actions.js +11 -0
  13. package/dist-client/utils/assistant-session-actions.js.map +1 -1
  14. package/dist-client/utils/assistant-session-admin.d.ts +32 -1
  15. package/dist-client/utils/assistant-session-admin.js +36 -5
  16. package/dist-client/utils/assistant-session-admin.js.map +1 -1
  17. package/dist-server/service/assistant-chat-resolver.js +1 -1
  18. package/dist-server/service/assistant-chat-resolver.js.map +1 -1
  19. package/dist-server/service/assistant-mode/index.d.ts +1 -1
  20. package/dist-server/service/assistant-mode/index.js +2 -1
  21. package/dist-server/service/assistant-mode/index.js.map +1 -1
  22. package/dist-server/service/assistant-mode/mode-categories.d.ts +15 -0
  23. package/dist-server/service/assistant-mode/mode-categories.js +7 -1
  24. package/dist-server/service/assistant-mode/mode-categories.js.map +1 -1
  25. package/dist-server/service/chat-session/session-cleanup.d.ts +28 -0
  26. package/dist-server/service/chat-session/session-cleanup.js +69 -0
  27. package/dist-server/service/chat-session/session-cleanup.js.map +1 -0
  28. package/dist-server/service/chat-session/session-inbox.d.ts +15 -0
  29. package/dist-server/service/chat-session/session-inbox.js +11 -0
  30. package/dist-server/service/chat-session/session-inbox.js.map +1 -1
  31. package/dist-server/service/chat-session/session-purge.d.ts +4 -0
  32. package/dist-server/service/chat-session/session-purge.js +45 -11
  33. package/dist-server/service/chat-session/session-purge.js.map +1 -1
  34. package/dist-server/service/chat-session-resolver.d.ts +24 -2
  35. package/dist-server/service/chat-session-resolver.js +193 -13
  36. package/dist-server/service/chat-session-resolver.js.map +1 -1
  37. package/dist-server/service/data-read/list-read.d.ts +3 -1
  38. package/dist-server/service/data-read/list-read.js +27 -2
  39. package/dist-server/service/data-read/list-read.js.map +1 -1
  40. package/dist-server/service/data-read/read-target.d.ts +16 -0
  41. package/dist-server/service/data-read/read-target.js +18 -1
  42. package/dist-server/service/data-read/read-target.js.map +1 -1
  43. package/dist-server/service/data-read/read-tools.js +3 -1
  44. package/dist-server/service/data-read/read-tools.js.map +1 -1
  45. package/dist-server/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +7 -7
  47. package/server/service/assistant-chat-resolver.ts +2 -2
  48. package/server/service/assistant-mode/conversation-category.test.ts +15 -2
  49. package/server/service/assistant-mode/index.ts +1 -1
  50. package/server/service/assistant-mode/mode-categories.ts +17 -1
  51. package/server/service/assistant-mode/open-subject-door.test.ts +3 -1
  52. package/server/service/chat-session/session-cleanup.test.ts +83 -0
  53. package/server/service/chat-session/session-cleanup.ts +69 -0
  54. package/server/service/chat-session/session-inbox.ts +13 -0
  55. package/server/service/chat-session/session-purge.ts +46 -10
  56. package/server/service/chat-session-resolver.ts +150 -14
  57. package/server/service/data-read/list-read.ts +30 -3
  58. package/server/service/data-read/list-status.test.ts +46 -0
  59. package/server/service/data-read/read-target.ts +32 -1
  60. package/server/service/data-read/read-tools.ts +2 -1
  61. package/test/assistant-dock.test.ts +12 -1
  62. package/test/assistant-session-manager.test.ts +53 -1
  63. package/test/open-doors.test.ts +3 -1
  64. package/translations/en.json +37 -1
  65. package/translations/es.json +37 -1
  66. package/translations/ja.json +37 -1
  67. package/translations/ko.json +37 -1
  68. package/translations/ms.json +37 -1
  69. package/translations/zh.json +37 -1
@@ -13,6 +13,7 @@ const graphql_scalars_1 = require("graphql-scalars");
13
13
  const auth_base_1 = require("@things-factory/auth-base");
14
14
  const get_domain_users_js_1 = require("@things-factory/auth-base/dist-server/utils/get-domain-users.js");
15
15
  const shell_1 = require("@things-factory/shell");
16
+ const env_1 = require("@things-factory/env");
16
17
  const typeorm_1 = require("typeorm");
17
18
  const chat_session_js_1 = require("./chat-session/chat-session.js");
18
19
  const session_anchor_js_1 = require("./chat-session/session-anchor.js");
@@ -28,6 +29,15 @@ const invite_notification_js_1 = require("./chat-session-participant/invite-noti
28
29
  const read_roster_js_1 = require("./chat-session-participant/read-roster.js");
29
30
  const participant_lookup_js_1 = require("./chat-session-participant/participant-lookup.js");
30
31
  const session_write_gate_js_1 = require("./chat-session/session-write-gate.js");
32
+ const session_cleanup_js_1 = require("./chat-session/session-cleanup.js");
33
+ const session_retention_js_1 = require("./chat-session/session-retention.js");
34
+ /** The keeping periods the config asks for (shared-dock.md §9-5, §2-7). */
35
+ function keeping() {
36
+ const options = env_1.config.get('ai-assistant', {}) ?? {};
37
+ return { deletedDays: (0, session_retention_js_1.retentionDaysOf)(options.sessionRetentionDays), archiveDays: (0, session_cleanup_js_1.archiveDaysOf)(options.sessionArchiveDays) };
38
+ }
39
+ /** One request reads the list once - the list, its total and its counts ask together and share it. */
40
+ const inboxReads = new WeakMap();
31
41
  /** 초대 알림 — 발송은 호스트의 기전을 그대로 쓴다(`sendInviteNotification`, 문 셋이 같은 한 곳). */
32
42
  async function notifyInvited(context, invited, inviter, sessionName) {
33
43
  await (0, invite_notification_js_1.sendInviteNotification)(context.state?.notify, invited, inviter, sessionName, undefined, (0, request_translator_js_1.requestTranslator)(context));
@@ -128,11 +138,61 @@ tslib_1.__decorate([
128
138
  (0, type_graphql_1.Field)({ description: 'Whether this viewer is the owner of the session - only the owner may change its visibility.' }),
129
139
  tslib_1.__metadata("design:type", Boolean)
130
140
  ], ChatSessionInboxEntry.prototype, "owner", void 0);
141
+ tslib_1.__decorate([
142
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Whether the session has had no activity for the archive period (config sessionArchiveDays, default 60) - left out of the list unless includeArchived or a search asks for it.' }),
143
+ tslib_1.__metadata("design:type", Boolean)
144
+ ], ChatSessionInboxEntry.prototype, "archived", void 0);
145
+ tslib_1.__decorate([
146
+ (0, type_graphql_1.Field)({ nullable: true, description: "Why the list suggests clearing this session: 'empty' (never asked anything) or 'inactive' (no activity for 30 days). Only on sessions this viewer created." }),
147
+ tslib_1.__metadata("design:type", String)
148
+ ], ChatSessionInboxEntry.prototype, "cleanupReason", void 0);
149
+ tslib_1.__decorate([
150
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'Whole days since the last activity, or since the start when there is none.' }),
151
+ tslib_1.__metadata("design:type", Number)
152
+ ], ChatSessionInboxEntry.prototype, "idleDays", void 0);
131
153
  ChatSessionInboxEntry = tslib_1.__decorate([
132
154
  (0, type_graphql_1.ObjectType)({
133
155
  description: 'Session list entry for the inbox — a session plus this viewer\'s unread state. Ordered by recent activity so a user can find where to go back to.'
134
156
  })
135
157
  ], ChatSessionInboxEntry);
158
+ let DeletedSessionEntry = class DeletedSessionEntry {
159
+ };
160
+ tslib_1.__decorate([
161
+ (0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Session id.' }),
162
+ tslib_1.__metadata("design:type", String)
163
+ ], DeletedSessionEntry.prototype, "id", void 0);
164
+ tslib_1.__decorate([
165
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Session name.' }),
166
+ tslib_1.__metadata("design:type", String)
167
+ ], DeletedSessionEntry.prototype, "name", void 0);
168
+ tslib_1.__decorate([
169
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Which conversation this session is - the registered mode kind.' }),
170
+ tslib_1.__metadata("design:type", String)
171
+ ], DeletedSessionEntry.prototype, "kind", void 0);
172
+ tslib_1.__decorate([
173
+ (0, type_graphql_1.Field)({ description: 'When it was deleted.' }),
174
+ tslib_1.__metadata("design:type", Date)
175
+ ], DeletedSessionEntry.prototype, "deletedAt", void 0);
176
+ tslib_1.__decorate([
177
+ (0, type_graphql_1.Field)({ nullable: true, description: 'When the daily job empties it for good; none when emptying is turned off.' }),
178
+ tslib_1.__metadata("design:type", Date)
179
+ ], DeletedSessionEntry.prototype, "purgesAt", void 0);
180
+ DeletedSessionEntry = tslib_1.__decorate([
181
+ (0, type_graphql_1.ObjectType)({ description: 'A session the caller deleted that is still kept - it can be brought back until it is emptied.' })
182
+ ], DeletedSessionEntry);
183
+ let SessionKeeping = class SessionKeeping {
184
+ };
185
+ tslib_1.__decorate([
186
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'Days a deleted session can still be brought back; none when deleted sessions are never emptied.' }),
187
+ tslib_1.__metadata("design:type", Number)
188
+ ], SessionKeeping.prototype, "deletedDays", void 0);
189
+ tslib_1.__decorate([
190
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'Days without activity after which a session is archived; none when archiving is off.' }),
191
+ tslib_1.__metadata("design:type", Number)
192
+ ], SessionKeeping.prototype, "archiveDays", void 0);
193
+ SessionKeeping = tslib_1.__decorate([
194
+ (0, type_graphql_1.ObjectType)({ description: 'How long conversations are kept - what the list says when it deletes or archives.' })
195
+ ], SessionKeeping);
136
196
  let ChatAttachmentInfo = class ChatAttachmentInfo {
137
197
  };
138
198
  exports.ChatAttachmentInfo = ChatAttachmentInfo;
@@ -203,9 +263,9 @@ let ChatSessionResolver = class ChatSessionResolver {
203
263
  * 집계 쿼리를 쓰지 않는다: 세션의 비정규화된 lastMessageAt 과 내 참여자 lastReadAt 을 **값 비교**한다
204
264
  * (5개 DB 드라이버 지원 때문에 group by/MAX 를 목록 경로에 두지 않는다).
205
265
  */
206
- async chatSessionInbox(anchorType, anchorId, context, showHidden, search, mineOnly, limit, offset, kind, participantOnly, visibility, unreadOnly) {
266
+ async chatSessionInbox(anchorType, anchorId, context, showHidden, search, mineOnly, limit, offset, kind, participantOnly, visibility, unreadOnly, includeArchived, cleanupOnly) {
207
267
  const filtered = await this._inboxFiltered(anchorType, anchorId, context, {
208
- showHidden, search, mineOnly, kind, participantOnly, visibility, unreadOnly
268
+ showHidden, search, mineOnly, kind, participantOnly, visibility, unreadOnly, includeArchived, cleanupOnly
209
269
  });
210
270
  const from = Math.max(0, offset ?? 0);
211
271
  const size = Math.min(Math.max(1, limit ?? 50), 200);
@@ -217,13 +277,15 @@ let ChatSessionResolver = class ChatSessionResolver {
217
277
  * 목록과 같은 요청에 담아 가져가므로 왕복은 늘지 않는다. 개수를 모르면 사용자는 목록의 끝이
218
278
  * 페이지의 끝인지 자료의 끝인지 구별할 수 없다.
219
279
  */
220
- async chatSessionInboxTotal(anchorType, anchorId, context, showHidden, search, mineOnly, kind, participantOnly, visibility, unreadOnly) {
221
- return (await this._inboxFiltered(anchorType, anchorId, context, { showHidden, search, mineOnly, kind, participantOnly, visibility, unreadOnly })).length;
280
+ async chatSessionInboxTotal(anchorType, anchorId, context, showHidden, search, mineOnly, kind, participantOnly, visibility, unreadOnly, includeArchived, cleanupOnly) {
281
+ return (await this._inboxFiltered(anchorType, anchorId, context, {
282
+ showHidden, search, mineOnly, kind, participantOnly, visibility, unreadOnly, includeArchived, cleanupOnly
283
+ })).length;
222
284
  }
223
285
  /** 필터 적용(페이지 전) — 목록·개수가 **같은 규칙**을 쓰도록 한 곳에 둔다(어긋나면 "더 보기" 가 거짓말이 된다). */
224
286
  async _inboxFiltered(anchorType, anchorId, context, opts) {
225
287
  const all = await this._inboxEntries(anchorType, anchorId, context);
226
- let entries = opts.showHidden ? all : all.filter(e => !e.hidden);
288
+ let entries = (0, session_inbox_js_1.listedEntries)(all, opts);
227
289
  entries = (0, session_inbox_js_1.filterInbox)(entries, opts);
228
290
  if (!opts.search?.trim() || !entries.length)
229
291
  return entries;
@@ -252,8 +314,68 @@ let ChatSessionResolver = class ChatSessionResolver {
252
314
  const all = await this._inboxEntries(anchorType, anchorId, context);
253
315
  return all.filter(e => e.hidden).length;
254
316
  }
317
+ /* Like the hidden count: an archived conversation must not be forgotten, so the list says how many wait there. */
318
+ async archivedSessionCount(anchorType, anchorId, context) {
319
+ return (await this._inboxEntries(anchorType, anchorId, context)).filter(e => e.archived && !e.hidden).length;
320
+ }
321
+ async cleanupSessionCount(anchorType, anchorId, context) {
322
+ return (await this._inboxEntries(anchorType, anchorId, context)).filter(e => e.cleanupReason).length;
323
+ }
324
+ aiSessionKeeping() {
325
+ const { deletedDays, archiveDays } = keeping();
326
+ return { deletedDays: deletedDays ?? undefined, archiveDays: archiveDays ?? undefined };
327
+ }
328
+ /** What I deleted here and can still bring back - newest first. Only the creator deletes, so only the creator sees them. */
329
+ async deletedAISessions(anchorType, anchorId, context) {
330
+ const { domain, user } = context.state;
331
+ if (!user?.id)
332
+ return [];
333
+ const { deletedDays } = keeping();
334
+ const rows = await (0, shell_1.getRepository)(chat_session_js_1.ChatSession).find({
335
+ where: { ...(0, session_anchor_js_1.anchorWhere)(domain.id, anchorType, anchorId), creator: { id: user.id }, deletedAt: (0, typeorm_1.Not)((0, typeorm_1.IsNull)()) },
336
+ withDeleted: true,
337
+ order: { deletedAt: 'DESC' },
338
+ take: session_inbox_js_1.INBOX_SCAN_CAP
339
+ });
340
+ return rows.map(row => ({
341
+ id: row.id,
342
+ name: row.name ?? undefined,
343
+ kind: row.kind ?? undefined,
344
+ deletedAt: row.deletedAt,
345
+ purgesAt: deletedDays === null ? undefined : new Date(new Date(row.deletedAt).getTime() + deletedDays * session_retention_js_1.DAY_MS)
346
+ }));
347
+ }
348
+ /** Bringing a deleted conversation back - its creator, while it is kept. Its messages and seats were never touched. */
349
+ async restoreAISession(sessionId, context) {
350
+ const { domain, user } = context.state;
351
+ const repo = (0, shell_1.getRepository)(chat_session_js_1.ChatSession);
352
+ const session = await repo.findOne({ where: { id: sessionId, domain: { id: domain.id } }, withDeleted: true });
353
+ if (!session || !session.deletedAt)
354
+ return false;
355
+ if (!user?.id || session.creatorId !== user.id) {
356
+ throw new Error('Only the user who created this session may bring it back.');
357
+ }
358
+ await repo.restore(sessionId);
359
+ (0, session_activity_publish_js_1.publishSessionActivity)({
360
+ domainId: domain.id,
361
+ sessionId,
362
+ kind: 'meta',
363
+ anchorType: session.anchorType,
364
+ anchorId: session.anchorId,
365
+ actorId: user.id
366
+ });
367
+ return true;
368
+ }
255
369
  /** 목록 조립 — 숨김 표시까지 붙인 **전체**. 목록·개수 두 진입점이 같은 규칙을 쓰도록 한 곳에 둔다. */
256
- async _inboxEntries(anchorType, anchorId, context) {
370
+ _inboxEntries(anchorType, anchorId, context) {
371
+ const reads = inboxReads.get(context) ?? new Map();
372
+ inboxReads.set(context, reads);
373
+ const key = `${anchorType}\u0000${anchorId}`;
374
+ if (!reads.has(key))
375
+ reads.set(key, this._readInbox(anchorType, anchorId, context));
376
+ return reads.get(key);
377
+ }
378
+ async _readInbox(anchorType, anchorId, context) {
257
379
  const { domain, user } = context.state;
258
380
  /* 조회 상한 — 한 앵커의 대화가 아무리 늘어도 한 번에 이만큼만 훑는다. 최근 활동순으로 자른다
259
381
  * (오래된 대화가 잘리는 것이 최근 대화가 잘리는 것보다 낫다). 정렬·검색·페이지는 순수 로직이 한다. */
@@ -273,10 +395,18 @@ let ChatSessionResolver = class ChatSessionResolver {
273
395
  const hiddenAt = new Map(rows.map(r => [r.sessionId, r.hiddenAt]));
274
396
  /* 내가 만든 대화인지 표시 — 삭제 버튼을 보일지 결정하는 UI 힌트(권한은 서버가 다시 판정한다). */
275
397
  const createdByMe = new Set(sessions.filter(s => s.creatorId === user?.id).map(s => s.id));
276
- const entries = (0, session_inbox_js_1.buildInbox)(sessions, participations).map(entry => ({
277
- ...entry,
278
- mine: createdByMe.has(entry.id)
279
- }));
398
+ const now = new Date();
399
+ const { archiveDays } = keeping();
400
+ const entries = (0, session_inbox_js_1.buildInbox)(sessions, participations).map(entry => {
401
+ const mine = createdByMe.has(entry.id);
402
+ return {
403
+ ...entry,
404
+ mine,
405
+ idleDays: (0, session_cleanup_js_1.idleDays)(entry, now),
406
+ archived: (0, session_cleanup_js_1.isArchived)(entry, now, archiveDays),
407
+ cleanupReason: (0, session_cleanup_js_1.cleanupReason)({ ...entry, mine }, now) ?? undefined
408
+ };
409
+ });
280
410
  /* showHidden=true 로 불러 **전체**를 얻는다 — 숨김 여부 판정을 두 곳에 복제하지 않기 위함. */
281
411
  const shown = (0, session_inbox_js_1.applyHidden)(entries, hiddenAt, true);
282
412
  /*
@@ -1018,8 +1148,10 @@ tslib_1.__decorate([
1018
1148
  tslib_1.__param(9, (0, type_graphql_1.Arg)('participantOnly', { nullable: true, description: 'Only conversations the caller is in.' })),
1019
1149
  tslib_1.__param(10, (0, type_graphql_1.Arg)('visibility', { nullable: true, description: "Only 'open' or only 'private'." })),
1020
1150
  tslib_1.__param(11, (0, type_graphql_1.Arg)('unreadOnly', { nullable: true, description: 'Only conversations with unread activity.' })),
1151
+ tslib_1.__param(12, (0, type_graphql_1.Arg)('includeArchived', { nullable: true, description: 'Also the archived ones - no activity for the archive period.' })),
1152
+ tslib_1.__param(13, (0, type_graphql_1.Arg)('cleanupOnly', { nullable: true, description: "Only the caller's own the list suggests clearing (empty, or inactive for 30 days), hidden and archived ones included." })),
1021
1153
  tslib_1.__metadata("design:type", Function),
1022
- tslib_1.__metadata("design:paramtypes", [String, String, Object, Boolean, String, Boolean, Number, Number, String, Boolean, String, Boolean]),
1154
+ tslib_1.__metadata("design:paramtypes", [String, String, Object, Boolean, String, Boolean, Number, Number, String, Boolean, String, Boolean, Boolean, Boolean]),
1023
1155
  tslib_1.__metadata("design:returntype", Promise)
1024
1156
  ], ChatSessionResolver.prototype, "chatSessionInbox", null);
1025
1157
  tslib_1.__decorate([
@@ -1037,8 +1169,10 @@ tslib_1.__decorate([
1037
1169
  tslib_1.__param(7, (0, type_graphql_1.Arg)('participantOnly', { nullable: true, description: 'Only conversations the caller is in.' })),
1038
1170
  tslib_1.__param(8, (0, type_graphql_1.Arg)('visibility', { nullable: true, description: "Only 'open' or only 'private'." })),
1039
1171
  tslib_1.__param(9, (0, type_graphql_1.Arg)('unreadOnly', { nullable: true, description: 'Only conversations with unread activity.' })),
1172
+ tslib_1.__param(10, (0, type_graphql_1.Arg)('includeArchived', { nullable: true, description: 'Also the archived ones - no activity for the archive period.' })),
1173
+ tslib_1.__param(11, (0, type_graphql_1.Arg)('cleanupOnly', { nullable: true, description: "Only the caller's own the list suggests clearing." })),
1040
1174
  tslib_1.__metadata("design:type", Function),
1041
- tslib_1.__metadata("design:paramtypes", [String, String, Object, Boolean, String, Boolean, String, Boolean, String, Boolean]),
1175
+ tslib_1.__metadata("design:paramtypes", [String, String, Object, Boolean, String, Boolean, String, Boolean, String, Boolean, Boolean, Boolean]),
1042
1176
  tslib_1.__metadata("design:returntype", Promise)
1043
1177
  ], ChatSessionResolver.prototype, "chatSessionInboxTotal", null);
1044
1178
  tslib_1.__decorate([
@@ -1053,9 +1187,55 @@ tslib_1.__decorate([
1053
1187
  tslib_1.__metadata("design:paramtypes", [String, String, Object]),
1054
1188
  tslib_1.__metadata("design:returntype", Promise)
1055
1189
  ], ChatSessionResolver.prototype, "hiddenSessionCount", null);
1190
+ tslib_1.__decorate([
1191
+ (0, type_graphql_1.Query)(() => type_graphql_1.Int, { description: 'Number of sessions on this anchor that are archived (no activity for the archive period) and not hidden by the caller.' }),
1192
+ (0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
1193
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
1194
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
1195
+ tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
1196
+ tslib_1.__metadata("design:type", Function),
1197
+ tslib_1.__metadata("design:paramtypes", [String, String, Object]),
1198
+ tslib_1.__metadata("design:returntype", Promise)
1199
+ ], ChatSessionResolver.prototype, "archivedSessionCount", null);
1200
+ tslib_1.__decorate([
1201
+ (0, type_graphql_1.Query)(() => type_graphql_1.Int, { description: "Number of the caller's own sessions on this anchor the list suggests clearing - empty, or inactive for 30 days." }),
1202
+ (0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
1203
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
1204
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
1205
+ tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
1206
+ tslib_1.__metadata("design:type", Function),
1207
+ tslib_1.__metadata("design:paramtypes", [String, String, Object]),
1208
+ tslib_1.__metadata("design:returntype", Promise)
1209
+ ], ChatSessionResolver.prototype, "cleanupSessionCount", null);
1210
+ tslib_1.__decorate([
1211
+ (0, type_graphql_1.Query)(() => SessionKeeping, { description: 'How long deleted sessions can be brought back and after how long an idle one is archived.' }),
1212
+ (0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
1213
+ tslib_1.__metadata("design:type", Function),
1214
+ tslib_1.__metadata("design:paramtypes", []),
1215
+ tslib_1.__metadata("design:returntype", SessionKeeping)
1216
+ ], ChatSessionResolver.prototype, "aiSessionKeeping", null);
1217
+ tslib_1.__decorate([
1218
+ (0, type_graphql_1.Query)(() => [DeletedSessionEntry], { description: 'Sessions on this anchor that the caller deleted and that are still kept, newest deletion first. They can be brought back with restoreAISession until they are emptied.' }),
1219
+ (0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
1220
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
1221
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
1222
+ tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
1223
+ tslib_1.__metadata("design:type", Function),
1224
+ tslib_1.__metadata("design:paramtypes", [String, String, Object]),
1225
+ tslib_1.__metadata("design:returntype", Promise)
1226
+ ], ChatSessionResolver.prototype, "deletedAISessions", null);
1227
+ tslib_1.__decorate([
1228
+ (0, type_graphql_1.Mutation)(() => Boolean, { description: 'Bring back a session the caller deleted, while it is still kept (before the daily job empties it). Creator only, like deleting.' }),
1229
+ (0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
1230
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
1231
+ tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
1232
+ tslib_1.__metadata("design:type", Function),
1233
+ tslib_1.__metadata("design:paramtypes", [String, Object]),
1234
+ tslib_1.__metadata("design:returntype", Promise)
1235
+ ], ChatSessionResolver.prototype, "restoreAISession", null);
1056
1236
  tslib_1.__decorate([
1057
1237
  (0, type_graphql_1.Mutation)(() => Boolean, {
1058
- description: 'Delete a chat session — creator only, since a shared deliberation cannot be un-deleted from the UI. Participants who are not the creator can hide it from their own list instead (hideAISession). Soft delete; broadcast as meta activity so every list drops it.'
1238
+ description: 'Delete a chat session — creator only, since it disappears for every participant. Participants who are not the creator can hide it from their own list instead (hideAISession). Soft delete: the creator can bring it back with restoreAISession until the daily job empties it (sessionRetentionDays). Broadcast as meta activity so every list drops it.'
1059
1239
  }),
1060
1240
  (0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
1061
1241
  tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),