@things-factory/ai-assistant 10.1.26 → 10.1.28

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 (194) hide show
  1. package/client/components/assistant-chat.ts +653 -59
  2. package/client/components/assistant-mode.test.ts +86 -55
  3. package/client/components/assistant-mode.ts +124 -82
  4. package/client/components/chat-echo-dedup.test.ts +23 -0
  5. package/client/components/chat-echo-dedup.ts +29 -3
  6. package/client/components/chat-input-builder.test.ts +20 -20
  7. package/client/components/chat-input-builder.ts +16 -14
  8. package/client/components/chat-proposal-survival.test.ts +73 -0
  9. package/client/components/mention-popup.ts +31 -8
  10. package/client/components/panel-aria.test.ts +34 -0
  11. package/client/components/proposal-outcome-line.ts +33 -0
  12. package/client/utils/assistant-session-controller.test.ts +75 -0
  13. package/client/utils/assistant-session-controller.ts +23 -0
  14. package/client/utils/assistant-session-transport.ts +11 -3
  15. package/dist-client/components/assistant-chat.d.ts +128 -13
  16. package/dist-client/components/assistant-chat.js +613 -67
  17. package/dist-client/components/assistant-chat.js.map +1 -1
  18. package/dist-client/components/assistant-mode.d.ts +80 -68
  19. package/dist-client/components/assistant-mode.js +63 -19
  20. package/dist-client/components/assistant-mode.js.map +1 -1
  21. package/dist-client/components/assistant-mode.test.js +74 -53
  22. package/dist-client/components/assistant-mode.test.js.map +1 -1
  23. package/dist-client/components/chat-echo-dedup.d.ts +2 -0
  24. package/dist-client/components/chat-echo-dedup.js +27 -4
  25. package/dist-client/components/chat-echo-dedup.js.map +1 -1
  26. package/dist-client/components/chat-echo-dedup.test.js +20 -0
  27. package/dist-client/components/chat-echo-dedup.test.js.map +1 -1
  28. package/dist-client/components/chat-input-builder.d.ts +12 -7
  29. package/dist-client/components/chat-input-builder.js +8 -10
  30. package/dist-client/components/chat-input-builder.js.map +1 -1
  31. package/dist-client/components/chat-input-builder.test.js +17 -18
  32. package/dist-client/components/chat-input-builder.test.js.map +1 -1
  33. package/dist-client/components/chat-proposal-survival.test.d.ts +1 -0
  34. package/dist-client/components/chat-proposal-survival.test.js +61 -0
  35. package/dist-client/components/chat-proposal-survival.test.js.map +1 -0
  36. package/dist-client/components/mention-popup.js +28 -8
  37. package/dist-client/components/mention-popup.js.map +1 -1
  38. package/dist-client/components/panel-aria.test.d.ts +1 -0
  39. package/dist-client/components/panel-aria.test.js +31 -0
  40. package/dist-client/components/panel-aria.test.js.map +1 -0
  41. package/dist-client/components/proposal-outcome-line.d.ts +9 -0
  42. package/dist-client/components/proposal-outcome-line.js +15 -0
  43. package/dist-client/components/proposal-outcome-line.js.map +1 -0
  44. package/dist-client/tsconfig.tsbuildinfo +1 -1
  45. package/dist-client/utils/assistant-session-controller.d.ts +10 -0
  46. package/dist-client/utils/assistant-session-controller.js +16 -0
  47. package/dist-client/utils/assistant-session-controller.js.map +1 -1
  48. package/dist-client/utils/assistant-session-controller.test.d.ts +1 -0
  49. package/dist-client/utils/assistant-session-controller.test.js +56 -0
  50. package/dist-client/utils/assistant-session-controller.test.js.map +1 -0
  51. package/dist-client/utils/assistant-session-transport.js +11 -3
  52. package/dist-client/utils/assistant-session-transport.js.map +1 -1
  53. package/dist-server/service/assistant-capability-resolver.d.ts +17 -0
  54. package/dist-server/service/assistant-capability-resolver.js +88 -0
  55. package/dist-server/service/assistant-capability-resolver.js.map +1 -0
  56. package/dist-server/service/assistant-chat-resolver.d.ts +6 -6
  57. package/dist-server/service/assistant-chat-resolver.js +166 -44
  58. package/dist-server/service/assistant-chat-resolver.js.map +1 -1
  59. package/dist-server/service/assistant-mode/assistant-mode-resolver.d.ts +25 -0
  60. package/dist-server/service/assistant-mode/assistant-mode-resolver.js +140 -0
  61. package/dist-server/service/assistant-mode/assistant-mode-resolver.js.map +1 -0
  62. package/dist-server/service/assistant-mode/index.d.ts +9 -0
  63. package/dist-server/service/assistant-mode/index.js +19 -0
  64. package/dist-server/service/assistant-mode/index.js.map +1 -0
  65. package/dist-server/service/assistant-mode/registry.d.ts +18 -0
  66. package/dist-server/service/assistant-mode/registry.js +136 -0
  67. package/dist-server/service/assistant-mode/registry.js.map +1 -0
  68. package/dist-server/service/assistant-mode/resolve.d.ts +33 -0
  69. package/dist-server/service/assistant-mode/resolve.js +60 -0
  70. package/dist-server/service/assistant-mode/resolve.js.map +1 -0
  71. package/dist-server/service/assistant-mode/types.d.ts +99 -0
  72. package/dist-server/service/assistant-mode/types.js +12 -0
  73. package/dist-server/service/assistant-mode/types.js.map +1 -0
  74. package/dist-server/service/chat-message/carried-turns.d.ts +48 -0
  75. package/dist-server/service/chat-message/carried-turns.js +80 -0
  76. package/dist-server/service/chat-message/carried-turns.js.map +1 -0
  77. package/dist-server/service/chat-message/chat-message-subscription.d.ts +1 -1
  78. package/dist-server/service/chat-message/chat-message-subscription.js +24 -6
  79. package/dist-server/service/chat-message/chat-message-subscription.js.map +1 -1
  80. package/dist-server/service/chat-message/chat-message.d.ts +32 -0
  81. package/dist-server/service/chat-message/chat-message.js +17 -0
  82. package/dist-server/service/chat-message/chat-message.js.map +1 -1
  83. package/dist-server/service/chat-message/door-access.d.ts +15 -0
  84. package/dist-server/service/chat-message/door-access.js +26 -0
  85. package/dist-server/service/chat-message/door-access.js.map +1 -0
  86. package/dist-server/service/chat-message/fold-session-history.d.ts +30 -0
  87. package/dist-server/service/chat-message/fold-session-history.js +133 -0
  88. package/dist-server/service/chat-message/fold-session-history.js.map +1 -0
  89. package/dist-server/service/chat-message/llm-history.d.ts +7 -0
  90. package/dist-server/service/chat-message/llm-history.js +23 -11
  91. package/dist-server/service/chat-message/llm-history.js.map +1 -1
  92. package/dist-server/service/chat-message/message-doors.d.ts +29 -0
  93. package/dist-server/service/chat-message/message-doors.js +107 -0
  94. package/dist-server/service/chat-message/message-doors.js.map +1 -0
  95. package/dist-server/service/chat-message/proposal-outcome.d.ts +18 -0
  96. package/dist-server/service/chat-message/proposal-outcome.js +31 -0
  97. package/dist-server/service/chat-message/proposal-outcome.js.map +1 -0
  98. package/dist-server/service/chat-message/reader-gate.d.ts +3 -0
  99. package/dist-server/service/chat-message/reader-gate.js +37 -0
  100. package/dist-server/service/chat-message/reader-gate.js.map +1 -0
  101. package/dist-server/service/chat-message/room-message.d.ts +4 -0
  102. package/dist-server/service/chat-message/room-message.js +25 -0
  103. package/dist-server/service/chat-message/room-message.js.map +1 -0
  104. package/dist-server/service/chat-session/chat-session.d.ts +24 -0
  105. package/dist-server/service/chat-session/chat-session.js +26 -2
  106. package/dist-server/service/chat-session/chat-session.js.map +1 -1
  107. package/dist-server/service/chat-session/session-inbox.d.ts +4 -1
  108. package/dist-server/service/chat-session/session-inbox.js +3 -2
  109. package/dist-server/service/chat-session/session-inbox.js.map +1 -1
  110. package/dist-server/service/chat-session/session-visibility.d.ts +15 -0
  111. package/dist-server/service/chat-session/session-visibility.js +28 -0
  112. package/dist-server/service/chat-session/session-visibility.js.map +1 -0
  113. package/dist-server/service/chat-session/station-session-guard.d.ts +1 -5
  114. package/dist-server/service/chat-session/station-session-guard.js +7 -2
  115. package/dist-server/service/chat-session/station-session-guard.js.map +1 -1
  116. package/dist-server/service/chat-session-participant/mention-invite.d.ts +29 -0
  117. package/dist-server/service/chat-session-participant/mention-invite.js +103 -0
  118. package/dist-server/service/chat-session-participant/mention-invite.js.map +1 -0
  119. package/dist-server/service/chat-session-resolver.d.ts +15 -0
  120. package/dist-server/service/chat-session-resolver.js +267 -15
  121. package/dist-server/service/chat-session-resolver.js.map +1 -1
  122. package/dist-server/service/grounding-sources.d.ts +14 -0
  123. package/dist-server/service/grounding-sources.js +26 -0
  124. package/dist-server/service/grounding-sources.js.map +1 -0
  125. package/dist-server/service/index.d.ts +8 -1
  126. package/dist-server/service/index.js +19 -0
  127. package/dist-server/service/index.js.map +1 -1
  128. package/dist-server/service/request-translator.d.ts +2 -0
  129. package/dist-server/service/request-translator.js +20 -0
  130. package/dist-server/service/request-translator.js.map +1 -0
  131. package/dist-server/service/testing/conversation-script.d.ts +123 -0
  132. package/dist-server/service/testing/conversation-script.js +202 -0
  133. package/dist-server/service/testing/conversation-script.js.map +1 -0
  134. package/dist-server/service/testing/mode-conformance.d.ts +40 -0
  135. package/dist-server/service/testing/mode-conformance.js +90 -0
  136. package/dist-server/service/testing/mode-conformance.js.map +1 -0
  137. package/dist-server/tool-registry-boot-report.d.ts +3 -0
  138. package/dist-server/tool-registry-boot-report.js +25 -2
  139. package/dist-server/tool-registry-boot-report.js.map +1 -1
  140. package/dist-server/tsconfig.tsbuildinfo +1 -1
  141. package/package.json +5 -5
  142. package/server/explicit-graphql-types.test.ts +56 -0
  143. package/server/service/assistant-capability-resolver.ts +66 -0
  144. package/server/service/assistant-chat-resolver.ts +184 -42
  145. package/server/service/assistant-mode/assistant-mode-resolver.ts +102 -0
  146. package/server/service/assistant-mode/index.ts +16 -0
  147. package/server/service/assistant-mode/registry.test.ts +105 -0
  148. package/server/service/assistant-mode/registry.ts +147 -0
  149. package/server/service/assistant-mode/resolve.test.ts +78 -0
  150. package/server/service/assistant-mode/resolve.ts +96 -0
  151. package/server/service/assistant-mode/subject-defaults.test.ts +86 -0
  152. package/server/service/assistant-mode/types.ts +103 -0
  153. package/server/service/chat-message/carried-turns.test.ts +69 -0
  154. package/server/service/chat-message/carried-turns.ts +99 -0
  155. package/server/service/chat-message/chat-message-subscription.ts +20 -4
  156. package/server/service/chat-message/chat-message.ts +44 -0
  157. package/server/service/chat-message/door-access.ts +45 -0
  158. package/server/service/chat-message/fold-session-history.test.ts +98 -0
  159. package/server/service/chat-message/fold-session-history.ts +115 -0
  160. package/server/service/chat-message/llm-history.test.ts +58 -7
  161. package/server/service/chat-message/llm-history.ts +30 -11
  162. package/server/service/chat-message/message-doors.test.ts +87 -0
  163. package/server/service/chat-message/message-doors.ts +117 -0
  164. package/server/service/chat-message/proposal-outcome.test.ts +48 -0
  165. package/server/service/chat-message/proposal-outcome.ts +42 -0
  166. package/server/service/chat-message/reader-gate.ts +42 -0
  167. package/server/service/chat-message/room-message.test.ts +21 -0
  168. package/server/service/chat-message/room-message.ts +22 -0
  169. package/server/service/chat-session/chat-session.ts +31 -1
  170. package/server/service/chat-session/session-inbox.test.ts +18 -0
  171. package/server/service/chat-session/session-inbox.ts +9 -2
  172. package/server/service/chat-session/session-visibility.test.ts +36 -0
  173. package/server/service/chat-session/session-visibility.ts +26 -0
  174. package/server/service/chat-session/station-session-guard.ts +10 -2
  175. package/server/service/chat-session-participant/mention-invite.test.ts +125 -0
  176. package/server/service/chat-session-participant/mention-invite.ts +121 -0
  177. package/server/service/chat-session-resolver.ts +272 -15
  178. package/server/service/grounding-sources.test.ts +55 -0
  179. package/server/service/grounding-sources.ts +46 -0
  180. package/server/service/index.ts +19 -0
  181. package/server/service/request-translator.test.ts +45 -0
  182. package/server/service/request-translator.ts +33 -0
  183. package/server/service/station-session-guard.test.ts +12 -5
  184. package/server/service/testing/conversation-scenario.test.ts +163 -0
  185. package/server/service/testing/conversation-script.test.ts +131 -0
  186. package/server/service/testing/conversation-script.ts +288 -0
  187. package/server/service/testing/mode-conformance.ts +107 -0
  188. package/server/tool-registry-boot-report.ts +25 -2
  189. package/test/assistant-chat-call-sites.test.ts +82 -0
  190. package/translations/en.json +44 -2
  191. package/translations/ja.json +44 -2
  192. package/translations/ko.json +44 -2
  193. package/translations/ms.json +44 -2
  194. package/translations/zh.json +44 -2
@@ -1,7 +1,3 @@
1
- /** Station authoring must never append its design snapshot to another subject's history.
2
- * This is a consistency check, not authorization; tenant/privilege checks stay in the resolver.
3
- * Older Board sessions have only boardId, so preserve that supported read shape.
4
- */
5
1
  export declare function assertStationSession(session: {
6
2
  anchorType?: string | null;
7
3
  anchorId?: string | null;
@@ -9,4 +5,4 @@ export declare function assertStationSession(session: {
9
5
  } | undefined, host: {
10
6
  surface?: string;
11
7
  boardId?: unknown;
12
- } | undefined): void;
8
+ } | undefined, t?: (key: string, params?: Record<string, any>) => string): void;
@@ -5,7 +5,10 @@ exports.assertStationSession = assertStationSession;
5
5
  * This is a consistency check, not authorization; tenant/privilege checks stay in the resolver.
6
6
  * Older Board sessions have only boardId, so preserve that supported read shape.
7
7
  */
8
- function assertStationSession(session, host) {
8
+ const STATION_MISMATCH = 'this conversation belongs to another station screen. open the conversation of the screen you are on.';
9
+ function assertStationSession(session, host,
10
+ /* The reader's sentence comes from the request (ADR-0054); without a translator it falls back to English. */
11
+ t) {
9
12
  if (host?.surface !== 'station-screen-modeller')
10
13
  return;
11
14
  const boardId = host.boardId;
@@ -15,6 +18,8 @@ function assertStationSession(session, host) {
15
18
  (session.anchorId || session.boardId) === boardId &&
16
19
  (!session.boardId || session.boardId === boardId);
17
20
  if (!valid)
18
- throw new Error('현재 Station Screen과 대화 이력이 일치하지 않습니다. 해당 화면의 대화를 다시 여세요.');
21
+ throw new Error(t
22
+ ? t('ai-assistant.text.other-station-history', { defaultValue: STATION_MISMATCH })
23
+ : STATION_MISMATCH);
19
24
  }
20
25
  //# sourceMappingURL=station-session-guard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"station-session-guard.js","sourceRoot":"","sources":["../../../server/service/chat-session/station-session-guard.ts"],"names":[],"mappings":";;AAIA,oDAYC;AAhBD;;;GAGG;AACH,SAAgB,oBAAoB,CAClC,OAAuF,EACvF,IAAkD;IAElD,IAAG,IAAI,EAAE,OAAO,KAAG,yBAAyB;QAAC,OAAM;IACnD,MAAM,OAAO,GAAC,IAAI,CAAC,OAAO,CAAA;IAC1B,MAAM,MAAM,GAAC,OAAO,CAAC,OAAO,IAAE,CAAC,OAAO,CAAC,UAAU,IAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IACnE,MAAM,KAAK,GAAC,OAAO,OAAO,KAAG,QAAQ,IAAE,OAAO,CAAC,MAAM,GAAC,CAAC,IAAE,OAAO;QAC9D,CAAC,OAAO,CAAC,UAAU,KAAG,OAAO,IAAE,MAAM,CAAC;QACtC,CAAC,OAAO,CAAC,QAAQ,IAAE,OAAO,CAAC,OAAO,CAAC,KAAG,OAAO;QAC7C,CAAC,CAAC,OAAO,CAAC,OAAO,IAAE,OAAO,CAAC,OAAO,KAAG,OAAO,CAAC,CAAA;IAC/C,IAAG,CAAC,KAAK;QAAC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;AACtF,CAAC","sourcesContent":["/** Station authoring must never append its design snapshot to another subject's history.\n * This is a consistency check, not authorization; tenant/privilege checks stay in the resolver.\n * Older Board sessions have only boardId, so preserve that supported read shape.\n */\nexport function assertStationSession(\n session: {anchorType?:string|null;anchorId?:string|null;boardId?:string|null}|undefined,\n host: {surface?:string;boardId?:unknown}|undefined\n) {\n if(host?.surface!=='station-screen-modeller')return\n const boardId=host.boardId\n const legacy=Boolean(session&&!session.anchorType&&session.boardId)\n const valid=typeof boardId==='string'&&boardId.length>0&&session&&\n (session.anchorType==='board'||legacy)&&\n (session.anchorId||session.boardId)===boardId&&\n (!session.boardId||session.boardId===boardId)\n if(!valid)throw new Error('현재 Station Screen과 대화 이력이 일치하지 않습니다. 해당 화면의 대화를 다시 여세요.')\n}\n"]}
1
+ {"version":3,"file":"station-session-guard.js","sourceRoot":"","sources":["../../../server/service/chat-session/station-session-guard.ts"],"names":[],"mappings":";;AAMA,oDAkBC;AAxBD;;;GAGG;AACH,MAAM,gBAAgB,GAAG,sGAAsG,CAAA;AAE/H,SAAgB,oBAAoB,CAClC,OAAuF,EACvF,IAAkD;AAClD,6GAA6G;AAC7G,CAAyD;IAEzD,IAAG,IAAI,EAAE,OAAO,KAAG,yBAAyB;QAAC,OAAM;IACnD,MAAM,OAAO,GAAC,IAAI,CAAC,OAAO,CAAA;IAC1B,MAAM,MAAM,GAAC,OAAO,CAAC,OAAO,IAAE,CAAC,OAAO,CAAC,UAAU,IAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IACnE,MAAM,KAAK,GAAC,OAAO,OAAO,KAAG,QAAQ,IAAE,OAAO,CAAC,MAAM,GAAC,CAAC,IAAE,OAAO;QAC9D,CAAC,OAAO,CAAC,UAAU,KAAG,OAAO,IAAE,MAAM,CAAC;QACtC,CAAC,OAAO,CAAC,QAAQ,IAAE,OAAO,CAAC,OAAO,CAAC,KAAG,OAAO;QAC7C,CAAC,CAAC,OAAO,CAAC,OAAO,IAAE,OAAO,CAAC,OAAO,KAAG,OAAO,CAAC,CAAA;IAC/C,IAAG,CAAC,KAAK;QAAC,MAAM,IAAI,KAAK,CACvB,CAAC;YACC,CAAC,CAAC,CAAC,CAAC,yCAAyC,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC;YAClF,CAAC,CAAC,gBAAgB,CACrB,CAAA;AACH,CAAC","sourcesContent":["/** Station authoring must never append its design snapshot to another subject's history.\n * This is a consistency check, not authorization; tenant/privilege checks stay in the resolver.\n * Older Board sessions have only boardId, so preserve that supported read shape.\n */\nconst STATION_MISMATCH = 'this conversation belongs to another station screen. open the conversation of the screen you are on.'\n\nexport function assertStationSession(\n session: {anchorType?:string|null;anchorId?:string|null;boardId?:string|null}|undefined,\n host: {surface?:string;boardId?:unknown}|undefined,\n /* The reader's sentence comes from the request (ADR-0054); without a translator it falls back to English. */\n t?: (key: string, params?: Record<string, any>) => string\n) {\n if(host?.surface!=='station-screen-modeller')return\n const boardId=host.boardId\n const legacy=Boolean(session&&!session.anchorType&&session.boardId)\n const valid=typeof boardId==='string'&&boardId.length>0&&session&&\n (session.anchorType==='board'||legacy)&&\n (session.anchorId||session.boardId)===boardId&&\n (!session.boardId||session.boardId===boardId)\n if(!valid)throw new Error(\n t\n ? t('ai-assistant.text.other-station-history', { defaultValue: STATION_MISMATCH })\n : STATION_MISMATCH\n )\n}\n"]}
@@ -0,0 +1,29 @@
1
+ /** 본문에서 `@user{userId:UUID}` 마커의 userId 를 뽑는다 — 첫 등장 순, 중복 제거. */
2
+ export declare function extractMentionUserIds(text: string | null | undefined): string[];
3
+ /** 멘션된 사람 중 **아직 참여자가 아니고 자기 자신도 아닌** 새 사람만. */
4
+ export declare function newParticipantUserIds(mentioned: ReadonlyArray<string>, existing: ReadonlyArray<string>, selfUserId?: string | null): string[];
5
+ export interface MentionInviteResult {
6
+ /** 이번에 새로 앉은 사람들. 알림은 이들에게 간다. */
7
+ invited: string[];
8
+ /** 비공개 대화라 앉히지 못한 사람들 — 이름은 불렸고 방에는 못 들어온다. 화면이 그대로 말한다. */
9
+ notSeated?: string[];
10
+ }
11
+ export interface MentionInviteInput {
12
+ /** 저장된 **사람의 말**. 어시스턴트의 답이나 도구 결과를 여기에 넘기지 않는다(조건 ①). */
13
+ content: string;
14
+ sessionId: string;
15
+ domainId: string;
16
+ inviter: {
17
+ id?: string;
18
+ } | undefined | null;
19
+ /** 참여자 저장소 — 시험이 가짜를 넣는다. */
20
+ participants: any;
21
+ /** 이 대화가 공개인가(ADR-0073 판정 2). 비공개면 이름만 알리고 앉히지 않는다. */
22
+ visibility?: string | null;
23
+ }
24
+ /**
25
+ * 멘션된 사람을 참여자로 앉힌다.
26
+ *
27
+ * 나갔던 사람(soft delete)은 되살린다 — 다시 부른 것이지 새 사람이 아니다.
28
+ */
29
+ export declare function inviteMentionedUsers(input: MentionInviteInput): Promise<MentionInviteResult>;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /*
3
+ * Mentioning someone tells them (ADR-0071 decision 1, as amended by ADR-0073).
4
+ *
5
+ * The machinery existed in the board product only: `@name` in a board chat added a participant and
6
+ * sent them an invite, and the same `@name` in a twin deliberation added nobody. The person typing
7
+ * it had every reason to believe they had called a colleague in.
8
+ *
9
+ * ── What a mention is now ─────────────────────────────────────────────────────
10
+ * A conversation in the domain is open: anyone may walk in, with nothing to approve and nobody to
11
+ * ask (ADR-0073 decision 2). So a mention grants nothing — it points. It seats the named person so
12
+ * the conversation reaches their list, and it tells them their name came up. Somebody who was never
13
+ * mentioned can read the same room by opening it.
14
+ *
15
+ * Two rules survive the amendment:
16
+ * ① only a door that stores **a person's turn** mentions — never the model, a tool, or the
17
+ * assistant's own answer. What the assistant writes must not seat people.
18
+ * ② whoever is named joins as a member and is told.
19
+ *
20
+ * What went: the inviter's own role (there is nothing to hand out), the refusal verdicts, and the
21
+ * notice shown before sending. A private conversation is the one place a mention cannot seat
22
+ * someone — there the named person is told and stays outside until an owner opens it.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.extractMentionUserIds = extractMentionUserIds;
26
+ exports.newParticipantUserIds = newParticipantUserIds;
27
+ exports.inviteMentionedUsers = inviteMentionedUsers;
28
+ /** 본문에서 `@user{userId:UUID}` 마커의 userId 를 뽑는다 — 첫 등장 순, 중복 제거. */
29
+ function extractMentionUserIds(text) {
30
+ if (!text)
31
+ return [];
32
+ const re = /\{userId:([^}\s]+)\}/g;
33
+ const seen = new Set();
34
+ const out = [];
35
+ let m;
36
+ while ((m = re.exec(text)) !== null) {
37
+ const id = m[1];
38
+ if (!seen.has(id)) {
39
+ seen.add(id);
40
+ out.push(id);
41
+ }
42
+ }
43
+ return out;
44
+ }
45
+ /** 멘션된 사람 중 **아직 참여자가 아니고 자기 자신도 아닌** 새 사람만. */
46
+ function newParticipantUserIds(mentioned, existing, selfUserId) {
47
+ const have = new Set(existing);
48
+ if (selfUserId)
49
+ have.add(selfUserId);
50
+ const out = [];
51
+ const seen = new Set();
52
+ for (const id of mentioned) {
53
+ if (!id || have.has(id) || seen.has(id))
54
+ continue;
55
+ seen.add(id);
56
+ out.push(id);
57
+ }
58
+ return out;
59
+ }
60
+ /**
61
+ * 멘션된 사람을 참여자로 앉힌다.
62
+ *
63
+ * 나갔던 사람(soft delete)은 되살린다 — 다시 부른 것이지 새 사람이 아니다.
64
+ */
65
+ async function inviteMentionedUsers(input) {
66
+ const mentioned = extractMentionUserIds(input.content);
67
+ if (mentioned.length === 0)
68
+ return { invited: [] };
69
+ const repo = input.participants;
70
+ const existing = await repo.find({
71
+ where: { session: { id: input.sessionId }, domain: { id: input.domainId } },
72
+ withDeleted: true
73
+ });
74
+ const activeIds = existing.filter((row) => !row.deletedAt).map((row) => row.userId).filter(Boolean);
75
+ const fresh = newParticipantUserIds(mentioned, activeIds, input.inviter?.id);
76
+ if (fresh.length === 0)
77
+ return { invited: [] };
78
+ /*
79
+ * 비공개 대화에서는 이름을 불러도 앉히지 않는다. 공개 대화에서는 앉히는 일이 아무것도 주지 않지만
80
+ * (누구나 들어온다), 비공개에서는 그것이 곧 접근을 주는 일이다. 부르는 사람이 아니라 owner 가 열
81
+ * 일이다 — 이름이 불렸다는 것만 알린다.
82
+ */
83
+ if ((input.visibility ?? 'open') !== 'open')
84
+ return { invited: [], notSeated: fresh };
85
+ for (const userId of fresh) {
86
+ const prior = existing.find((row) => row.userId === userId && row.deletedAt);
87
+ if (prior) {
88
+ await repo.update(prior.id, { deletedAt: null, role: 'member', updater: input.inviter });
89
+ }
90
+ else {
91
+ await repo.save({
92
+ domain: { id: input.domainId },
93
+ session: { id: input.sessionId },
94
+ user: { id: userId },
95
+ role: 'member',
96
+ creator: input.inviter,
97
+ updater: input.inviter
98
+ });
99
+ }
100
+ }
101
+ return { invited: fresh };
102
+ }
103
+ //# sourceMappingURL=mention-invite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention-invite.js","sourceRoot":"","sources":["../../../server/service/chat-session-participant/mention-invite.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AAGH,sDAcC;AAGD,sDAeC;AA0BD,oDAsCC;AAjGD,kEAAkE;AAClE,SAAgB,qBAAqB,CAAC,IAA+B;IACnE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IACpB,MAAM,EAAE,GAAG,uBAAuB,CAAA;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,IAAI,CAAyB,CAAA;IAC7B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACf,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACZ,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACd,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,gDAAgD;AAChD,SAAgB,qBAAqB,CACnC,SAAgC,EAChC,QAA+B,EAC/B,UAA0B;IAE1B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC9B,IAAI,UAAU;QAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACpC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAQ;QACjD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACZ,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAqBD;;;;GAIG;AACI,KAAK,UAAU,oBAAoB,CAAC,KAAyB;IAClE,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACtD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IAElD,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAA;IAC/B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAS,EAAE;QACzF,WAAW,EAAE,IAAI;KAClB,CAAC,CAAA;IAEF,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC7G,MAAM,KAAK,GAAG,qBAAqB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAC5E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IAE9C;;;;OAIG;IACH,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,KAAK,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IAErF,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;QACjF,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAS,CAAC,CAAA;QACjG,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAS;gBACrC,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAS;gBACvC,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAS;gBAC3B,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;aAChB,CAAC,CAAA;QACX,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAC3B,CAAC","sourcesContent":["/*\n * Mentioning someone tells them (ADR-0071 decision 1, as amended by ADR-0073).\n *\n * The machinery existed in the board product only: `@name` in a board chat added a participant and\n * sent them an invite, and the same `@name` in a twin deliberation added nobody. The person typing\n * it had every reason to believe they had called a colleague in.\n *\n * ── What a mention is now ─────────────────────────────────────────────────────\n * A conversation in the domain is open: anyone may walk in, with nothing to approve and nobody to\n * ask (ADR-0073 decision 2). So a mention grants nothing — it points. It seats the named person so\n * the conversation reaches their list, and it tells them their name came up. Somebody who was never\n * mentioned can read the same room by opening it.\n *\n * Two rules survive the amendment:\n * ① only a door that stores **a person's turn** mentions — never the model, a tool, or the\n * assistant's own answer. What the assistant writes must not seat people.\n * ② whoever is named joins as a member and is told.\n *\n * What went: the inviter's own role (there is nothing to hand out), the refusal verdicts, and the\n * notice shown before sending. A private conversation is the one place a mention cannot seat\n * someone — there the named person is told and stays outside until an owner opens it.\n */\n\n/** 본문에서 `@user{userId:UUID}` 마커의 userId 를 뽑는다 — 첫 등장 순, 중복 제거. */\nexport function extractMentionUserIds(text: string | null | undefined): string[] {\n if (!text) return []\n const re = /\\{userId:([^}\\s]+)\\}/g\n const seen = new Set<string>()\n const out: string[] = []\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const id = m[1]\n if (!seen.has(id)) {\n seen.add(id)\n out.push(id)\n }\n }\n return out\n}\n\n/** 멘션된 사람 중 **아직 참여자가 아니고 자기 자신도 아닌** 새 사람만. */\nexport function newParticipantUserIds(\n mentioned: ReadonlyArray<string>,\n existing: ReadonlyArray<string>,\n selfUserId?: string | null\n): string[] {\n const have = new Set(existing)\n if (selfUserId) have.add(selfUserId)\n const out: string[] = []\n const seen = new Set<string>()\n for (const id of mentioned) {\n if (!id || have.has(id) || seen.has(id)) continue\n seen.add(id)\n out.push(id)\n }\n return out\n}\n\nexport interface MentionInviteResult {\n /** 이번에 새로 앉은 사람들. 알림은 이들에게 간다. */\n invited: string[]\n /** 비공개 대화라 앉히지 못한 사람들 — 이름은 불렸고 방에는 못 들어온다. 화면이 그대로 말한다. */\n notSeated?: string[]\n}\n\nexport interface MentionInviteInput {\n /** 저장된 **사람의 말**. 어시스턴트의 답이나 도구 결과를 여기에 넘기지 않는다(조건 ①). */\n content: string\n sessionId: string\n domainId: string\n inviter: { id?: string } | undefined | null\n /** 참여자 저장소 — 시험이 가짜를 넣는다. */\n participants: any\n /** 이 대화가 공개인가(ADR-0073 판정 2). 비공개면 이름만 알리고 앉히지 않는다. */\n visibility?: string | null\n}\n\n/**\n * 멘션된 사람을 참여자로 앉힌다.\n *\n * 나갔던 사람(soft delete)은 되살린다 — 다시 부른 것이지 새 사람이 아니다.\n */\nexport async function inviteMentionedUsers(input: MentionInviteInput): Promise<MentionInviteResult> {\n const mentioned = extractMentionUserIds(input.content)\n if (mentioned.length === 0) return { invited: [] }\n\n const repo = input.participants\n const existing = await repo.find({\n where: { session: { id: input.sessionId } as any, domain: { id: input.domainId } as any },\n withDeleted: true\n })\n\n const activeIds = existing.filter((row: any) => !row.deletedAt).map((row: any) => row.userId).filter(Boolean)\n const fresh = newParticipantUserIds(mentioned, activeIds, input.inviter?.id)\n if (fresh.length === 0) return { invited: [] }\n\n /*\n * 비공개 대화에서는 이름을 불러도 앉히지 않는다. 공개 대화에서는 앉히는 일이 아무것도 주지 않지만\n * (누구나 들어온다), 비공개에서는 그것이 곧 접근을 주는 일이다. 부르는 사람이 아니라 owner 가 열\n * 일이다 — 이름이 불렸다는 것만 알린다.\n */\n if ((input.visibility ?? 'open') !== 'open') return { invited: [], notSeated: fresh }\n\n for (const userId of fresh) {\n const prior = existing.find((row: any) => row.userId === userId && row.deletedAt)\n if (prior) {\n await repo.update(prior.id, { deletedAt: null, role: 'member', updater: input.inviter } as any)\n } else {\n await repo.save({\n domain: { id: input.domainId } as any,\n session: { id: input.sessionId } as any,\n user: { id: userId } as any,\n role: 'member',\n creator: input.inviter,\n updater: input.inviter\n } as any)\n }\n }\n\n return { invited: fresh }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { User } from '@things-factory/auth-base';
2
2
  import { ChatSession } from './chat-session/chat-session.js';
3
+ import { type ProposalOutcome } from './chat-message/proposal-outcome.js';
3
4
  import { ChatMessage } from './chat-message/chat-message.js';
4
5
  import { ChatSessionParticipant } from './chat-session-participant/chat-session-participant.js';
5
6
  declare class ChatSessionInboxEntry {
@@ -10,6 +11,8 @@ declare class ChatSessionInboxEntry {
10
11
  boardId?: string;
11
12
  lastMessageAt?: Date;
12
13
  lastMessagePreview?: string;
14
+ /** 미리보기가 이 사람에게 가려져 나갔는가 — 목록도 대화와 같은 관문을 지난다(ADR-0073 판정 1). */
15
+ previewRedacted?: boolean;
13
16
  createdAt?: Date;
14
17
  unread: boolean;
15
18
  participant: boolean;
@@ -71,6 +74,16 @@ export declare class ChatSessionResolver {
71
74
  * 변경은 **meta 활동**으로 방송한다 — 모든 참여자 목록에 즉시 반영되지만 안 읽음 배지는 올리지 않는다
72
75
  * (이름 변경을 "새 소식" 으로 알리면 소음이다).
73
76
  */
77
+ /**
78
+ * 이 대화를 도메인 안 누구나 읽게 할 것인가 — ADR-0073 판정 2.
79
+ *
80
+ * owner 만 바꾼다. 이름 바꾸기는 어느 참여자나 하지만(되돌릴 수 있고 값을 드러내지 않는다), 공개로
81
+ * 바꾸는 일은 이미 쓰인 글을 도메인 전체에 여는 일이다 — 되돌려도 읽힌 것은 읽힌 것이다.
82
+ *
83
+ * 화면은 공개로 바꾸기 전에 한 줄을 먼저 보인다(문구는 클라이언트가 가진다). 그 한 줄은 이 문이
84
+ * 강제하지 않는다 — 문이 막을 수 있는 것은 누가 바꾸느냐까지다.
85
+ */
86
+ setSessionVisibility(sessionId: string, visibility: string, context: ResolverContext): Promise<boolean>;
74
87
  renameAISession(sessionId: string, name: string, context: ResolverContext): Promise<boolean>;
75
88
  /**
76
89
  * 읽은 지점 갱신 — 이 세션을 여기까지 읽었다. 멱등(여러 번 호출해도 최신 시각으로만 이동).
@@ -84,6 +97,8 @@ export declare class ChatSessionResolver {
84
97
  startAISession(anchorType: string, anchorId: string, context: ResolverContext): Promise<ChatSession>;
85
98
  createAISession(anchorType: string, anchorId: string, name: string | undefined, context: ResolverContext): Promise<ChatSession>;
86
99
  renameChatSession(sessionId: string, name: string, context: ResolverContext): Promise<boolean>;
100
+ reportProposalOutcome(sessionId: string, proposalKey: string, outcome: ProposalOutcome, context: ResolverContext): Promise<boolean>;
101
+ sayToRoom(sessionId: string, content: string, context: ResolverContext): Promise<string | null>;
87
102
  appendSessionNote(sessionId: string, content: string, context: ResolverContext): Promise<boolean>;
88
103
  appendSessionTurns(sessionId: string, turns: {
89
104
  role?: string;