@things-factory/board-ai 10.0.0 โ†’ 10.0.2

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 (67) hide show
  1. package/client/components/board-ai-chat.ts +107 -2
  2. package/client/components/chat-echo-dedup.ts +3 -0
  3. package/client/components/chat-input-builder.ts +6 -0
  4. package/dist-client/client/components/board-ai-chat.d.ts +17 -0
  5. package/dist-client/client/components/board-ai-chat.js +102 -2
  6. package/dist-client/client/components/board-ai-chat.js.map +1 -1
  7. package/dist-client/client/components/chat-echo-dedup.d.ts +2 -0
  8. package/dist-client/client/components/chat-echo-dedup.js +1 -0
  9. package/dist-client/client/components/chat-echo-dedup.js.map +1 -1
  10. package/dist-client/client/components/chat-input-builder.d.ts +5 -0
  11. package/dist-client/client/components/chat-input-builder.js +1 -0
  12. package/dist-client/client/components/chat-input-builder.js.map +1 -1
  13. package/dist-client/server/service/agentic-loop.d.ts +18 -0
  14. package/dist-client/server/service/agentic-loop.js +10 -1
  15. package/dist-client/server/service/agentic-loop.js.map +1 -1
  16. package/dist-client/server/service/assistant.js +14 -2
  17. package/dist-client/server/service/assistant.js.map +1 -1
  18. package/dist-client/server/service/grounding.d.ts +17 -0
  19. package/dist-client/server/service/grounding.js +42 -0
  20. package/dist-client/server/service/grounding.js.map +1 -0
  21. package/dist-client/server/service/types.d.ts +16 -0
  22. package/dist-client/server/service/types.js.map +1 -1
  23. package/dist-client/tsconfig.tsbuildinfo +1 -1
  24. package/dist-server/service/agentic-loop.d.ts +18 -0
  25. package/dist-server/service/agentic-loop.js +10 -1
  26. package/dist-server/service/agentic-loop.js.map +1 -1
  27. package/dist-server/service/assistant.js +14 -2
  28. package/dist-server/service/assistant.js.map +1 -1
  29. package/dist-server/service/board-ai-resolver.d.ts +2 -0
  30. package/dist-server/service/board-ai-resolver.js +22 -1
  31. package/dist-server/service/board-ai-resolver.js.map +1 -1
  32. package/dist-server/service/chat-message/chat-message.d.ts +12 -0
  33. package/dist-server/service/chat-message/chat-message.js +23 -0
  34. package/dist-server/service/chat-message/chat-message.js.map +1 -1
  35. package/dist-server/service/chat-session/session-inbox.d.ts +26 -0
  36. package/dist-server/service/chat-session/session-inbox.js +41 -0
  37. package/dist-server/service/chat-session/session-inbox.js.map +1 -1
  38. package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +11 -0
  39. package/dist-server/service/chat-session-participant/chat-session-participant.js +17 -1
  40. package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -1
  41. package/dist-server/service/chat-session-resolver.d.ts +44 -1
  42. package/dist-server/service/chat-session-resolver.js +306 -6
  43. package/dist-server/service/chat-session-resolver.js.map +1 -1
  44. package/dist-server/service/grounding.d.ts +17 -0
  45. package/dist-server/service/grounding.js +46 -0
  46. package/dist-server/service/grounding.js.map +1 -0
  47. package/dist-server/service/types.d.ts +16 -0
  48. package/dist-server/service/types.js.map +1 -1
  49. package/dist-server/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +5 -5
  51. package/server/service/agentic-loop.test.ts +63 -0
  52. package/server/service/agentic-loop.ts +28 -1
  53. package/server/service/assistant.ts +15 -2
  54. package/server/service/board-ai-resolver.ts +23 -1
  55. package/server/service/chat-message/chat-message.ts +26 -0
  56. package/server/service/chat-session/session-inbox.test.ts +69 -1
  57. package/server/service/chat-session/session-inbox.ts +45 -0
  58. package/server/service/chat-session-participant/chat-session-participant.ts +14 -0
  59. package/server/service/chat-session-resolver.ts +297 -5
  60. package/server/service/grounding.test.ts +55 -0
  61. package/server/service/grounding.ts +53 -0
  62. package/server/service/types.ts +16 -0
  63. package/translations/en.json +3 -1
  64. package/translations/ja.json +3 -1
  65. package/translations/ko.json +2 -0
  66. package/translations/ms.json +3 -1
  67. package/translations/zh.json +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chat-session-resolver.js","sourceRoot":"","sources":["../../server/service/chat-session-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ChatSession lifecycle resolver โ€” start/get/recordDirectPatch/revertPatch + ๋ฉ”์‹œ์ง€ยทํŒจ์น˜ ํŽ˜์ด์ง• query.\n *\n * ํ•ต์‹ฌ: `recordDirectPatch` โ€” ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋ธ๋Ÿฌ์—์„œ ์ง์ ‘ ํŽธ์ง‘ ์‹œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœ.\n * ์ด๊ฒŒ PatchEntry(source:'user-direct') + system ๋ฉ”์‹œ์ง€ ์ž๋™ ์ถ”๊ฐ€ โ†’ AI ๊ฐ€ ๋ชจ๋“  ๋ณ€๊ฒฝ์„ ์ธ์ง€.\n */\nimport { Resolver, Query, Mutation, Arg, Ctx, Directive, Int, ObjectType, Field, ID } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport { User } from '@things-factory/auth-base'\nimport { buildDomainUsersQueryBuilder } from '@things-factory/auth-base/dist-server/utils/get-domain-users.js'\n\nimport { getRepository } from '@things-factory/shell'\nimport { In } from 'typeorm'\n\nimport { ChatSession } from './chat-session/chat-session.js'\nimport { anchorColumns, anchorWhere } from './chat-session/session-anchor.js'\nimport { buildInbox, activityPatch } from './chat-session/session-inbox.js'\nimport { publishSessionActivity } from './chat-session/session-activity-publish.js'\nimport { ChatMessage } from './chat-message/chat-message.js'\nimport { ChatSessionParticipant } from './chat-session-participant/chat-session-participant.js'\nimport { PatchEntry } from './patch-entry/patch-entry.js'\nimport { publishBoardPatch } from './patch-entry/board-patch-subscription.js'\nimport { publishChatMessage } from './chat-message/chat-message-publish.js'\n\n@ObjectType({\n description:\n '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.'\n})\nclass ChatSessionInboxEntry {\n @Field(type => ID, { nullable: true, description: 'Session id.' })\n id?: string\n\n @Field({ nullable: true, description: 'User-given session name (tab label).' })\n name?: string\n\n @Field({ nullable: true, description: \"What the session is anchored to: 'board' | 'space' | 'instance' | 'none'.\" })\n anchorType?: string\n\n @Field({ nullable: true, description: 'Id of the anchored target.' })\n anchorId?: string\n\n @Field({ nullable: true, description: 'Connected board id (board-anchored sessions).' })\n boardId?: string\n\n @Field({ nullable: true, description: 'Timestamp of the last message (list ordering / unread comparison).' })\n lastMessageAt?: Date\n\n @Field({ nullable: true, description: 'Short preview of the last message.' })\n lastMessagePreview?: string\n\n @Field({ nullable: true, description: 'Session creation timestamp.' })\n createdAt?: Date\n\n @Field({ description: 'Whether this viewer has unread activity in the session.' })\n unread!: boolean\n\n @Field({ description: 'Whether this viewer is a participant of the session (non-participants get no unread state).' })\n participant!: boolean\n}\n\n@Resolver()\nexport class ChatSessionResolver {\n @Query(() => ChatSession, { nullable: true, description: 'Get AI chat session by id.' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSession(\n @Arg('id') id: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession | null> {\n const { domain } = context.state\n return (await getRepository(ChatSession).findOneBy({ id, domain: { id: domain.id } })) ?? null\n }\n\n @Query(() => ChatSession, { nullable: true, description: 'Get AI chat session by board id (returns first match โ€” backward compat single-session lookup).' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionByBoard(\n @Arg('boardId') boardId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession | null> {\n const { domain } = context.state\n return (\n (await getRepository(ChatSession).findOneBy({ boardId, domain: { id: domain.id } })) ?? null\n )\n }\n\n @Query(() => [ChatSession], {\n description:\n 'List AI chat sessions anchored to a target (oldest first). Anchor decides the nature and the history boundary of the conversation: board = authoring, space = the site and the twins running there. Board anchor also matches legacy rows that only have boardId.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionsByAnchor(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession[]> {\n const { domain } = context.state\n return await getRepository(ChatSession).find({\n where: anchorWhere(domain.id, anchorType, anchorId),\n order: { createdAt: 'ASC' }\n })\n }\n\n /** ๋ณด๋“œ ์ „์šฉ ์ง„์ž…์  โ€” ์•ต์ปค ์กฐํšŒ์˜ ์–‡์€ ๋ž˜ํผ(๊ธฐ์กด ์†Œ๋น„์ž ๋ฌด๋ณ€๊ฒฝ). */\n @Query(() => [ChatSession], {\n description: 'List all AI chat sessions for a board (oldest first). Thin wrapper over chatSessionsByAnchor(board).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionsByBoard(\n @Arg('boardId') boardId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession[]> {\n return await this.chatSessionsByAnchor('board', boardId, context)\n }\n\n /**\n * ๋ฐ›์€ํŽธ์ง€ํ•จ โ€” ์•ต์ปค์˜ ์„ธ์…˜ ๋ชฉ๋ก + ๋‚ด ์•ˆ ์ฝ์Œ ์ƒํƒœ. ์ตœ๊ทผ ํ™œ๋™์ˆœ.\n * ์ง‘๊ณ„ ์ฟผ๋ฆฌ๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค: ์„ธ์…˜์˜ ๋น„์ •๊ทœํ™”๋œ lastMessageAt ๊ณผ ๋‚ด ์ฐธ์—ฌ์ž lastReadAt ์„ **๊ฐ’ ๋น„๊ต**ํ•œ๋‹ค\n * (5๊ฐœ DB ๋“œ๋ผ์ด๋ฒ„ ์ง€์› ๋•Œ๋ฌธ์— group by/MAX ๋ฅผ ๋ชฉ๋ก ๊ฒฝ๋กœ์— ๋‘์ง€ ์•Š๋Š”๋‹ค).\n */\n @Query(() => [ChatSessionInboxEntry], {\n description:\n \"List sessions anchored to a target with this viewer's unread state, ordered by recent activity. Uses the denormalized last-activity timestamp compared against the viewer's read point โ€” no per-session aggregation.\"\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionInbox(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSessionInboxEntry[]> {\n const { domain, user } = context.state\n const sessions = await getRepository(ChatSession).find({\n where: anchorWhere(domain.id, anchorType, anchorId)\n })\n if (sessions.length === 0) return []\n const mine = await getRepository(ChatSessionParticipant).find({\n where: { domain: { id: domain.id } as any, user: { id: user.id } as any, session: { id: In(sessions.map(s => s.id!)) } as any }\n })\n return buildInbox(sessions as any, mine as any) as any\n }\n\n /**\n * ์„ธ์…˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ โ€” **์ฐธ์—ฌ์ž ๋ˆ„๊ตฌ๋‚˜** ํ•  ์ˆ˜ ์žˆ๋‹ค.\n *\n * ์™œ ์†Œ์œ ์ž ์ „์šฉ์ด ์•„๋‹Œ๊ฐ€: ์ด๋ฆ„์€ ๋Œ€ํ™”์˜ ์ฃผ์ œํ‘œ์ด๊ณ  ํ˜‘์˜๋Š” ์—ฌ๋Ÿฟ์ด ํ•œ๋‹ค. ๋…ผ์˜๊ฐ€ ํ˜๋Ÿฌ ์ฃผ์ œ๊ฐ€ ๋‹ฌ๋ผ์กŒ๋Š”๋ฐ\n * ๋งŒ๋“  ์‚ฌ๋žŒ๋งŒ ๊ณ ์น  ์ˆ˜ ์žˆ์œผ๋ฉด ๊ทธ ์‚ฌ๋žŒ์ด ์—†์„ ๋•Œ ๋ชฉ๋ก์ด ๊ณ„์† ์–ด๊ธ‹๋‚œ๋‹ค. ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋ณ€๊ฒฝ์ด๊ณ  ๋Œ€ํ™”\n * ๋‚ด์šฉ์— ์˜ํ–ฅ์ด ์—†์œผ๋ฏ€๋กœ ๊ถŒํ•œ์„ ์ขํž ์ด์œ ๊ฐ€ ์—†๋‹ค(๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋Š” ๊ฒƒ๋งŒ ์ขํžŒ๋‹ค).\n *\n * ๋ณ€๊ฒฝ์€ **meta ํ™œ๋™**์œผ๋กœ ๋ฐฉ์†กํ•œ๋‹ค โ€” ๋ชจ๋“  ์ฐธ์—ฌ์ž ๋ชฉ๋ก์— ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์ง€๋งŒ ์•ˆ ์ฝ์Œ ๋ฐฐ์ง€๋Š” ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค\n * (์ด๋ฆ„ ๋ณ€๊ฒฝ์„ \"์ƒˆ ์†Œ์‹\" ์œผ๋กœ ์•Œ๋ฆฌ๋ฉด ์†Œ์Œ์ด๋‹ค).\n */\n @Mutation(() => Boolean, {\n description:\n '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.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async renameAISession(\n @Arg('sessionId') sessionId: string,\n @Arg('name') name: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user } = context.state\n const trimmed = (name ?? '').trim().slice(0, 200)\n if (!trimmed) return false // ๋นˆ ์ด๋ฆ„์œผ๋กœ ์ง€์šฐ์ง€ ์•Š๋Š”๋‹ค โ€” ์ง€์šฐ๋ ค๋ฉด ๋ณ„๋„ ์˜๋„๊ฐ€ ํ•„์š”ํ•˜๋‹ค\n const participant = await getRepository(ChatSessionParticipant).findOne({\n where: { domain: { id: domain.id } as any, session: { id: sessionId } as any, user: { id: user.id } as any }\n })\n if (!participant) return false // ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ๋‚จ์˜ ๋Œ€ํ™” ์ œ๋ชฉ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋‹ค\n const session = await getRepository(ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } as any })\n if (!session) return false\n await getRepository(ChatSession).update(sessionId, { name: trimmed, updater: user } as any)\n publishSessionActivity({\n domainId: domain.id,\n sessionId,\n kind: 'meta',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n actorId: user?.id\n })\n return true\n }\n\n /**\n * ์ฝ์€ ์ง€์  ๊ฐฑ์‹  โ€” ์ด ์„ธ์…˜์„ ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค. ๋ฉฑ๋“ฑ(์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•ด๋„ ์ตœ์‹  ์‹œ๊ฐ์œผ๋กœ๋งŒ ์ด๋™).\n * ์ ‘์† ํ‘œ์‹œ(lastSeenAt)์™€ ๋ถ„๋ฆฌํ•œ๋‹ค โ€” ์ฐฝ์„ ์—ด์–ด ๋‘๊ณ  ํ™”๋ฉด์„ ์•ˆ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.\n */\n @Mutation(() => Boolean, {\n description:\n \"Mark a session as read up to now for the current user. Idempotent. Distinct from presence (lastSeenAt) โ€” a window can be open without being read.\"\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async markSessionRead(\n @Arg('sessionId') sessionId: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user } = context.state\n const repo = getRepository(ChatSessionParticipant)\n const mine = await repo.findOne({\n where: { domain: { id: domain.id } as any, session: { id: sessionId } as any, user: { id: user.id } as any }\n })\n /* ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์ฝ์Œ ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ์ฐธ์—ฌํ•˜์ง€ ์•Š์€ ๋Œ€ํ™”์— ๋‚ด ์ฝ์Œ ์ง€์ ์„ ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. */\n if (!mine) return false\n await repo.update(mine.id!, { lastReadAt: new Date(), updater: user } as any)\n return true\n }\n\n @Query(() => [User], {\n description:\n 'List users in the current domain for `@` mention popup. board-ai ๊ถŒํ•œ์ด๋ฉด ๋ˆ„๊ตฌ๋‚˜ ๋ฉ˜์…˜์šฉ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅ (๊ด€๋ฆฌ์ž ์ „์šฉ users() ์™€ ๋ณ„๋„).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async domainUsersForMention(\n @Arg('query', { nullable: true, description: 'Substring to match against name/email (case-insensitive). Empty โ†’ all.' }) query: string | undefined,\n @Arg('limit', () => Int, { nullable: true, defaultValue: 50 }) limit: number,\n @Ctx() context: ResolverContext\n ): Promise<User[]> {\n const { domain } = context.state\n const qb = buildDomainUsersQueryBuilder(domain.id, 'USER')\n if (query?.trim()) {\n const q = `%${query.trim().toLowerCase()}%`\n qb.andWhere('(LOWER(USER.name) LIKE :q OR LOWER(USER.email) LIKE :q)', { q })\n }\n qb.orderBy('USER.name', 'ASC').limit(limit)\n return await qb.getMany()\n }\n\n @Query(() => [ChatSessionParticipant], {\n description: 'List participants of a ChatSession (members / owner).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionParticipants(\n @Arg('sessionId') sessionId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSessionParticipant[]> {\n const { domain } = context.state\n const session = await getRepository(ChatSession).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) return []\n return await getRepository(ChatSessionParticipant).find({\n where: { session: { id: sessionId } as any, domain: { id: domain.id } },\n order: { createdAt: 'ASC' }\n })\n }\n\n @Mutation(() => Boolean, {\n description: 'Presence heartbeat โ€” bump current user\\'s lastSeenAt in the session (online indicator).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async touchPresence(@Arg('sessionId') sessionId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, user } = context.state\n const repo = getRepository(ChatSessionParticipant)\n const part = await repo.findOne({\n where: { session: { id: sessionId } as any, user: { id: user.id } as any, domain: { id: domain.id } as any }\n })\n if (!part) return false // ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด heartbeat ๋ฌด์‹œ (์ดˆ๋Œ€ ์ „)\n await repo.update(part.id!, { lastSeenAt: new Date(), updater: user } as any)\n return true\n }\n\n @Query(() => [ChatMessage], { description: 'List chat messages of a session, oldest first.' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatMessages(\n @Arg('sessionId') sessionId: string,\n @Arg('limit', () => Int, { nullable: true, defaultValue: 100 }) limit: number,\n @Arg('offset', () => Int, { nullable: true, defaultValue: 0 }) offset: number,\n @Ctx() context: ResolverContext\n ): Promise<ChatMessage[]> {\n const { domain } = context.state\n const session = await getRepository(ChatSession).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) return []\n return await getRepository(ChatMessage).find({\n where: { session: { id: sessionId } as any },\n // creator: ๊ทธ๋ฃน์ฑ— ๋ฐœ์‹ ์ž ํ‘œ์‹œ(name) + ์ž๊ธฐ ์—ฌ๋ถ€ ํŒ์ •(email). ๋‚ด๋ถ€ ๊ด€๋ฆฌํ‚ค(id)์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.\n relations: { creator: true },\n order: { createdAt: 'ASC' },\n take: limit,\n skip: offset\n })\n }\n\n @Query(() => [PatchEntry], { description: 'List patch entries of a session, newest first.' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatPatches(\n @Arg('sessionId') sessionId: string,\n @Arg('limit', () => Int, { nullable: true, defaultValue: 100 }) limit: number,\n @Ctx() context: ResolverContext\n ): Promise<PatchEntry[]> {\n const { domain } = context.state\n const session = await getRepository(ChatSession).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) return []\n return await getRepository(PatchEntry).find({\n where: { session: { id: sessionId } as any },\n order: { createdAt: 'DESC' },\n take: limit\n })\n }\n\n @Directive('@transaction')\n @Mutation(() => ChatSession, {\n description:\n 'Start (or get existing) AI chat session anchored to a target. Idempotent โ€” returns first existing or creates one. Use anchorType board for board authoring, space for a site (its co-located twins).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async startAISession(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession> {\n const { domain, user, tx } = context.state\n const repo = getRepository(ChatSession, tx)\n\n const existing = await repo.findOneBy(anchorWhere(domain.id, anchorType, anchorId))\n if (existing) return existing\n\n const session = await repo.save({\n domain,\n ...anchorColumns(anchorType, anchorId),\n creator: user,\n updater: user\n } as any)\n // creator ๋ฅผ owner participant ๋กœ ์ž๋™ ๋“ฑ๋ก โ€” ๋‹ค์ž๊ฐ„ ์ฑ„ํŒ… ๊ธฐ๋ฐ˜.\n await getRepository(ChatSessionParticipant, tx).save({\n domain,\n session: { id: session.id } as any,\n user,\n role: 'owner',\n creator: user,\n updater: user\n } as any)\n return session\n }\n\n /** ๋ณด๋“œ ์ „์šฉ ์ง„์ž…์  โ€” ์•ต์ปค ์‹œ์ž‘์˜ ์–‡์€ ๋ž˜ํผ(๊ธฐ์กด ์†Œ๋น„์ž ๋ฌด๋ณ€๊ฒฝ). */\n @Mutation(() => ChatSession, {\n description: 'Start (or get existing) AI chat session for a board. Thin wrapper over startAISession(board).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async startBoardAISession(\n @Arg('boardId') boardId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession> {\n return await this.startAISession('board', boardId, context)\n }\n\n @Directive('@transaction')\n @Mutation(() => ChatSession, {\n description:\n 'Always create a new AI chat session anchored to a target (no idempotent reuse). For multi-session UX โ€” ์ƒˆ ํƒญ ์—ด๊ธฐ.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async createAISession(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Arg('name', { nullable: true, description: 'Optional name (defaults to auto-generated `์„ธ์…˜ N`).' }) name: string | undefined,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession> {\n const { domain, user, tx } = context.state\n const repo = getRepository(ChatSession, tx)\n\n // ์ด๋ฆ„ ๋ฏธ์ง€์ • ์‹œ ์ž๋™ ์ƒ์„ฑ โ€” **๊ฐ™์€ ์•ต์ปค**์˜ ๊ธฐ์กด ์„ธ์…˜ ์ˆ˜ + 1.\n let finalName = name?.trim()\n if (!finalName) {\n const count = await repo.count({ where: anchorWhere(domain.id, anchorType, anchorId) })\n finalName = `์„ธ์…˜ ${count + 1}`\n }\n\n const session = await repo.save({\n domain,\n ...anchorColumns(anchorType, anchorId),\n creator: user,\n updater: user,\n name: finalName\n } as any)\n // creator ๋ฅผ owner participant ๋กœ ์ž๋™ ๋“ฑ๋ก.\n await getRepository(ChatSessionParticipant, tx).save({\n domain,\n session: { id: session.id } as any,\n user,\n role: 'owner',\n creator: user,\n updater: user\n } as any)\n return session\n }\n\n @Directive('@transaction')\n @Mutation(() => Boolean, {\n description: 'Rename a ChatSession (tab label).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async renameChatSession(\n @Arg('sessionId') sessionId: string,\n @Arg('name') name: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user, tx } = context.state\n const repo = getRepository(ChatSession, tx)\n const session = await repo.findOneBy({ id: sessionId, domain: { id: domain.id } })\n if (!session) throw new Error(`ChatSession ${sessionId} not found`)\n await repo.update(sessionId, { name: name.trim() || null, updater: user } as any)\n return true\n }\n\n @Directive('@transaction')\n @Mutation(() => PatchEntry, {\n description: 'Record a patch from user direct edit. Adds a system message so AI sees the change next turn.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async recordDirectPatch(\n @Arg('sessionId') sessionId: string,\n @Arg('ops', () => GraphQLJSON) ops: any[],\n @Arg('summary', { nullable: true }) summary: string | undefined,\n @Ctx() context: ResolverContext\n ): Promise<PatchEntry> {\n const { domain, tx, user } = context.state\n const session = await getRepository(ChatSession, tx).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) throw new Error(`ChatSession ${sessionId} not found`)\n\n const entry = await getRepository(PatchEntry, tx).save({\n session: { id: session.id } as any,\n source: 'user-direct',\n ops: JSON.stringify(ops || []),\n summary,\n reverted: false\n } as any)\n\n // AI ์ปจํ…์ŠคํŠธ ํ๋ฆ„ โ€” system ๋ฉ”์‹œ์ง€ ์ž๋™ ์ถ”๊ฐ€. creator = ์ง์ ‘ ํŽธ์ง‘ํ•œ ์‚ฌ์šฉ์ž.\n // parentMessage = ์ง์ „ in-session ๋ฉ”์‹œ์ง€ (timeline ์—ฐ์†์„ฑ ์œ ์ง€ โ€” system ๋„ chain ์— ํ•ฉ๋ฅ˜).\n const previousLast = await getRepository(ChatMessage, tx).findOne({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'DESC' }\n })\n const sysMsg = await getRepository(ChatMessage, tx).save({\n session: { id: session.id } as any,\n role: 'system',\n content: `User directly edited the board: ${summary ?? `${(ops || []).length} op(s)`}`,\n relatedPatchId: entry.id,\n creator: user,\n updater: user,\n parentMessage: previousLast ? ({ id: previousLast.id } as any) : undefined\n } as any)\n\n /* ์„ธ์…˜ ํ™œ๋™ ๊ฐฑ์‹  โ€” ์ง์ ‘ ํŽธ์ง‘๋„ ๋‹ค๋ฅธ ์ฐธ์—ฌ์ž์—๊ฒŒ๋Š” ์ƒˆ ์†Œ์‹์ด๋‹ค(๋ชฉ๋ก ์ •๋ ฌยท์•ˆ ์ฝ์Œ ํŒ์ •). */\n try {\n await getRepository(ChatSession, tx).update(session.id!, activityPatch(sysMsg.content) as any)\n } catch {\n /* best-effort โ€” ํŽธ์ง‘ ๊ธฐ๋ก์„ ๋ง‰์ง€ ์•Š๋Š”๋‹ค */\n }\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: sysMsg.content,\n actorId: user?.id\n })\n\n // C1: user-direct ํŽธ์ง‘๋„ ๊ฐ™์€ board ๊ตฌ๋…์ž์—๊ฒŒ ๋ฐฉ์†ก (AIยทํƒ€์ฐธ์—ฌ์ž ์‹ค์‹œ๊ฐ„ ๋ฐ˜์˜). best-effort.\n publishBoardPatch({ domainId: domain.id, boardId: session.boardId, patch: entry })\n // C4: ์ง์ ‘ํŽธ์ง‘ system ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ™์€ ์„ธ์…˜ ๊ตฌ๋…์ž์—๊ฒŒ ๋ฐฉ์†ก (transcript ์‹ค์‹œ๊ฐ„ ์ผ๊ด€์„ฑ). best-effort.\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: sysMsg })\n\n return entry\n }\n\n @Directive('@transaction')\n @Mutation(() => Boolean, { description: 'Mark a patch as reverted (does not undo, only flags).' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async revertPatch(\n @Arg('patchId') patchId: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx } = context.state\n await getRepository(PatchEntry, tx).update(patchId, { reverted: true })\n return true\n }\n}\n"]}
1
+ {"version":3,"file":"chat-session-resolver.js","sourceRoot":"","sources":["../../server/service/chat-session-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ChatSession lifecycle resolver โ€” start/get/recordDirectPatch/revertPatch + ๋ฉ”์‹œ์ง€ยทํŒจ์น˜ ํŽ˜์ด์ง• query.\n *\n * ํ•ต์‹ฌ: `recordDirectPatch` โ€” ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋ธ๋Ÿฌ์—์„œ ์ง์ ‘ ํŽธ์ง‘ ์‹œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœ.\n * ์ด๊ฒŒ PatchEntry(source:'user-direct') + system ๋ฉ”์‹œ์ง€ ์ž๋™ ์ถ”๊ฐ€ โ†’ AI ๊ฐ€ ๋ชจ๋“  ๋ณ€๊ฒฝ์„ ์ธ์ง€.\n */\nimport { Resolver, Query, Mutation, Arg, Ctx, Directive, Int, ObjectType, Field, ID } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport { User } from '@things-factory/auth-base'\nimport { buildDomainUsersQueryBuilder } from '@things-factory/auth-base/dist-server/utils/get-domain-users.js'\n\nimport { getRepository } from '@things-factory/shell'\nimport { In } from 'typeorm'\n\nimport { ChatSession } from './chat-session/chat-session.js'\nimport { anchorColumns, anchorWhere } from './chat-session/session-anchor.js'\nimport { buildInbox, applyHidden, activityPatch, searchInbox, INBOX_SCAN_CAP } from './chat-session/session-inbox.js'\nimport { publishSessionActivity } from './chat-session/session-activity-publish.js'\nimport { ChatMessage } from './chat-message/chat-message.js'\nimport { ChatSessionParticipant } from './chat-session-participant/chat-session-participant.js'\nimport { PatchEntry } from './patch-entry/patch-entry.js'\nimport { publishBoardPatch } from './patch-entry/board-patch-subscription.js'\nimport { publishChatMessage } from './chat-message/chat-message-publish.js'\n\n@ObjectType({\n description:\n '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.'\n})\nclass ChatSessionInboxEntry {\n @Field(type => ID, { nullable: true, description: 'Session id.' })\n id?: string\n\n @Field({ nullable: true, description: 'User-given session name (tab label).' })\n name?: string\n\n @Field({ nullable: true, description: \"What the session is anchored to: 'board' | 'space' | 'instance' | 'none'.\" })\n anchorType?: string\n\n @Field({ nullable: true, description: 'Id of the anchored target.' })\n anchorId?: string\n\n @Field({ nullable: true, description: 'Connected board id (board-anchored sessions).' })\n boardId?: string\n\n @Field({ nullable: true, description: 'Timestamp of the last message (list ordering / unread comparison).' })\n lastMessageAt?: Date\n\n @Field({ nullable: true, description: 'Short preview of the last message.' })\n lastMessagePreview?: string\n\n @Field({ nullable: true, description: 'Session creation timestamp.' })\n createdAt?: Date\n\n @Field({ description: 'Whether this viewer has unread activity in the session.' })\n unread!: boolean\n\n @Field({ description: 'Whether this viewer is a participant of the session (non-participants get no unread state).' })\n participant!: boolean\n\n /* ์‚ญ์ œ๋Š” ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์œผ๋ฏ€๋กœ **๋งŒ๋“  ์‚ฌ๋žŒ**๋งŒ ํ•œ๋‹ค โ€” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฒ„ํŠผ์„ ๋ณด์ผ์ง€ ํŒ๋‹จํ•˜๋Š” ๊ทผ๊ฑฐ.\n * (๊ถŒํ•œ ํŒ์ •์€ ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ํ•œ๋‹ค. ์ด ๊ฐ’์€ UI ํžŒํŠธ์ผ ๋ฟ์ด๋‹ค.) */\n @Field({ description: 'Whether this viewer created the session โ€” only the creator may delete it.' })\n mine!: boolean\n\n /* ์ˆจ๊น€์€ ๋‚˜์—๊ฒŒ๋งŒ ์ ์šฉ๋˜๊ณ  ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค โ€” ํ•„ํ„ฐ๋ฅผ ์ผ  ์‚ฌ์šฉ์ž๊ฐ€ ๋ฌด์—‡์ด ์ˆจ๊ฒจ์ง„ ๊ฒƒ์ธ์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค. */\n @Field({ nullable: true, description: 'Whether this viewer hid the session from their own list (only returned when showHidden is on).' })\n hidden?: boolean\n}\n\n@Resolver()\nexport class ChatSessionResolver {\n @Query(() => ChatSession, { nullable: true, description: 'Get AI chat session by id.' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSession(\n @Arg('id') id: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession | null> {\n const { domain } = context.state\n return (await getRepository(ChatSession).findOneBy({ id, domain: { id: domain.id } })) ?? null\n }\n\n @Query(() => ChatSession, { nullable: true, description: 'Get AI chat session by board id (returns first match โ€” backward compat single-session lookup).' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionByBoard(\n @Arg('boardId') boardId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession | null> {\n const { domain } = context.state\n return (\n (await getRepository(ChatSession).findOneBy({ boardId, domain: { id: domain.id } })) ?? null\n )\n }\n\n @Query(() => [ChatSession], {\n description:\n 'List AI chat sessions anchored to a target (oldest first). Anchor decides the nature and the history boundary of the conversation: board = authoring, space = the site and the twins running there. Board anchor also matches legacy rows that only have boardId.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionsByAnchor(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession[]> {\n const { domain } = context.state\n return await getRepository(ChatSession).find({\n where: anchorWhere(domain.id, anchorType, anchorId),\n order: { createdAt: 'ASC' }\n })\n }\n\n /** ๋ณด๋“œ ์ „์šฉ ์ง„์ž…์  โ€” ์•ต์ปค ์กฐํšŒ์˜ ์–‡์€ ๋ž˜ํผ(๊ธฐ์กด ์†Œ๋น„์ž ๋ฌด๋ณ€๊ฒฝ). */\n @Query(() => [ChatSession], {\n description: 'List all AI chat sessions for a board (oldest first). Thin wrapper over chatSessionsByAnchor(board).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionsByBoard(\n @Arg('boardId') boardId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession[]> {\n return await this.chatSessionsByAnchor('board', boardId, context)\n }\n\n /**\n * ๋ฐ›์€ํŽธ์ง€ํ•จ โ€” ์•ต์ปค์˜ ์„ธ์…˜ ๋ชฉ๋ก + ๋‚ด ์•ˆ ์ฝ์Œ ์ƒํƒœ. ์ตœ๊ทผ ํ™œ๋™์ˆœ.\n * ์ง‘๊ณ„ ์ฟผ๋ฆฌ๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค: ์„ธ์…˜์˜ ๋น„์ •๊ทœํ™”๋œ lastMessageAt ๊ณผ ๋‚ด ์ฐธ์—ฌ์ž lastReadAt ์„ **๊ฐ’ ๋น„๊ต**ํ•œ๋‹ค\n * (5๊ฐœ DB ๋“œ๋ผ์ด๋ฒ„ ์ง€์› ๋•Œ๋ฌธ์— group by/MAX ๋ฅผ ๋ชฉ๋ก ๊ฒฝ๋กœ์— ๋‘์ง€ ์•Š๋Š”๋‹ค).\n */\n @Query(() => [ChatSessionInboxEntry], {\n description:\n \"List sessions anchored to a target with this viewer's unread state, most recent activity first. Supports free-text search over name and last preview, a mine-only filter, hidden inclusion, and paging. Uses the denormalized last-activity timestamp compared against the viewer's read point โ€” no per-session aggregation.\"\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionInbox(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext,\n /* ์ˆจ๊ธด ๋Œ€ํ™” ๋ณด๊ธฐ โ€” ์ˆจ๊น€์ด ํ•œ ๋ฒˆ ๋„ฃ์œผ๋ฉด ๋ชป ๊บผ๋‚ด๋Š” ํ•จ์ •์ด ๋˜์ง€ ์•Š๋„๋ก ํ•„ํ„ฐ๋ฅผ ์ค€๋‹ค. */\n @Arg('showHidden', { nullable: true }) showHidden?: boolean,\n /* ๋Œ€๋Ÿ‰ ๋ชฉ๋ก ๋Œ€์‘ โ€” ๊ฒ€์ƒ‰ยท์†Œ์œ  ํ•„ํ„ฐยทํŽ˜์ด์ง€. ๋ชฉ๋ก UI ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์ด๊ฑธ ๊ฐ–๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค. */\n @Arg('search', { nullable: true }) search?: string,\n @Arg('mineOnly', { nullable: true }) mineOnly?: boolean,\n @Arg('limit', () => Int, { nullable: true }) limit?: number,\n @Arg('offset', () => Int, { nullable: true }) offset?: number\n ): Promise<ChatSessionInboxEntry[]> {\n const filtered = await this._inboxFiltered(anchorType, anchorId, context, { showHidden, search, mineOnly })\n const from = Math.max(0, offset ?? 0)\n const size = Math.min(Math.max(1, limit ?? 50), 200)\n return filtered.slice(from, from + size) as any\n }\n\n /**\n * ํ•„ํ„ฐ๋ฅผ ํ†ต๊ณผํ•œ **์ „์ฒด ๊ฐœ์ˆ˜** โ€” \"๋” ๋ณด๊ธฐ\" ๊ฐ€ ๋‚จ์•˜๋Š”์ง€, ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ๋ช‡ ๊ฑด์ธ์ง€.\n *\n * ๋ชฉ๋ก๊ณผ ๊ฐ™์€ ์š”์ฒญ์— ๋‹ด์•„ ๊ฐ€์ ธ๊ฐ€๋ฏ€๋กœ ์™•๋ณต์€ ๋Š˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐœ์ˆ˜๋ฅผ ๋ชจ๋ฅด๋ฉด ์‚ฌ์šฉ์ž๋Š” ๋ชฉ๋ก์˜ ๋์ด\n * ํŽ˜์ด์ง€์˜ ๋์ธ์ง€ ์ž๋ฃŒ์˜ ๋์ธ์ง€ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์—†๋‹ค.\n */\n @Query(() => Int, {\n description:\n 'Total number of sessions matching the same filters as chatSessionInbox (before paging). Request it alongside the list so the UI can tell \"end of page\" from \"end of data\".'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionInboxTotal(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext,\n @Arg('showHidden', { nullable: true }) showHidden?: boolean,\n @Arg('search', { nullable: true }) search?: string,\n @Arg('mineOnly', { nullable: true }) mineOnly?: boolean\n ): Promise<number> {\n return (await this._inboxFiltered(anchorType, anchorId, context, { showHidden, search, mineOnly })).length\n }\n\n /** ํ•„ํ„ฐ ์ ์šฉ(ํŽ˜์ด์ง€ ์ „) โ€” ๋ชฉ๋กยท๊ฐœ์ˆ˜๊ฐ€ **๊ฐ™์€ ๊ทœ์น™**์„ ์“ฐ๋„๋ก ํ•œ ๊ณณ์— ๋‘”๋‹ค(์–ด๊ธ‹๋‚˜๋ฉด \"๋” ๋ณด๊ธฐ\" ๊ฐ€ ๊ฑฐ์ง“๋ง์ด ๋œ๋‹ค). */\n private async _inboxFiltered(\n anchorType: string,\n anchorId: string,\n context: ResolverContext,\n opts: { showHidden?: boolean; search?: string; mineOnly?: boolean }\n ): Promise<ChatSessionInboxEntry[]> {\n const all = await this._inboxEntries(anchorType, anchorId, context)\n let entries = opts.showHidden ? all : all.filter(e => !e.hidden)\n if (opts.mineOnly) entries = entries.filter(e => (e as any).mine)\n return searchInbox(entries as any, opts.search) as any\n }\n\n /**\n * ๋‚ด๊ฐ€ ์ˆจ๊ธด ๋Œ€ํ™” ์ˆ˜ โ€” ํ•„ํ„ฐ์— \"๋ช‡ ๊ฐœ๊ฐ€ ์ ‘ํ˜€ ์žˆ๋Š”์ง€\" ๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๊ฐ’.\n *\n * ์ˆจ๊ธด ๊ฒƒ์ด ๋ช‡ ๊ฐœ์ธ์ง€ ๋ชจ๋ฅด๋ฉด ํ•„ํ„ฐ๋ฅผ ๋ˆŒ๋Ÿฌ ๋ณผ ์ด์œ ๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š๋Š”๋‹ค(์ˆจ๊ธด ์‚ฌ์‹ค ์ž์ฒด๋ฅผ ์žŠ๋Š”๋‹ค).\n * ๋ชฉ๋ก ์กฐํšŒ์™€ **๊ฐ™์€ ์š”์ฒญ**์— ๋‹ด์•„ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์™•๋ณต์€ ๋Š˜์ง€ ์•Š๋Š”๋‹ค.\n */\n @Query(() => Int, {\n description:\n \"Number of sessions on this anchor that the caller has hidden and that have had no activity since. Meant to be requested alongside chatSessionInbox in the same operation so the filter can say how many are folded away.\"\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async hiddenSessionCount(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext\n ): Promise<number> {\n const all = await this._inboxEntries(anchorType, anchorId, context)\n return all.filter(e => e.hidden).length\n }\n\n /** ๋ชฉ๋ก ์กฐ๋ฆฝ โ€” ์ˆจ๊น€ ํ‘œ์‹œ๊นŒ์ง€ ๋ถ™์ธ **์ „์ฒด**. ๋ชฉ๋กยท๊ฐœ์ˆ˜ ๋‘ ์ง„์ž…์ ์ด ๊ฐ™์€ ๊ทœ์น™์„ ์“ฐ๋„๋ก ํ•œ ๊ณณ์— ๋‘”๋‹ค. */\n private async _inboxEntries(\n anchorType: string,\n anchorId: string,\n context: ResolverContext\n ): Promise<ChatSessionInboxEntry[]> {\n const { domain, user } = context.state\n /* ์กฐํšŒ ์ƒํ•œ โ€” ํ•œ ์•ต์ปค์˜ ๋Œ€ํ™”๊ฐ€ ์•„๋ฌด๋ฆฌ ๋Š˜์–ด๋„ ํ•œ ๋ฒˆ์— ์ด๋งŒํผ๋งŒ ํ›‘๋Š”๋‹ค. ์ตœ๊ทผ ํ™œ๋™์ˆœ์œผ๋กœ ์ž๋ฅธ๋‹ค\n * (์˜ค๋ž˜๋œ ๋Œ€ํ™”๊ฐ€ ์ž˜๋ฆฌ๋Š” ๊ฒƒ์ด ์ตœ๊ทผ ๋Œ€ํ™”๊ฐ€ ์ž˜๋ฆฌ๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ๋‹ค). ์ •๋ ฌยท๊ฒ€์ƒ‰ยทํŽ˜์ด์ง€๋Š” ์ˆœ์ˆ˜ ๋กœ์ง์ด ํ•œ๋‹ค. */\n const sessions = await getRepository(ChatSession).find({\n where: anchorWhere(domain.id, anchorType, anchorId),\n order: { lastMessageAt: 'DESC' },\n take: INBOX_SCAN_CAP\n })\n if (sessions.length === 0) return []\n const rows = await getRepository(ChatSessionParticipant).find({\n where: { domain: { id: domain.id } as any, user: { id: user.id } as any, session: { id: In(sessions.map(s => s.id!)) } as any }\n })\n /* 'viewer' ๋Š” ๋Œ€ํ™”์— ๋‚€ ์‚ฌ๋žŒ์ด ์•„๋‹ˆ๋‹ค โ€” ์ˆจ๊น€์„ ๊ธฐ๋กํ•˜๋ ค๊ณ  ๋งŒ๋“  ํ–‰์ด๋ฏ€๋กœ ์ฝ์Œยท์•ˆ ์ฝ์Œ ํŒ์ •์—์„œ ๋บ€๋‹ค.\n * (์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹Œ ์‚ฌ๋žŒ์—๊ฒŒ ๋‚จ์˜ ํ˜‘์˜ ๋ฐฐ์ง€๋ฅผ ๋„์šฐ์ง€ ์•Š๋Š”๋‹ค.) */\n const participations = rows.filter(r => r.role !== 'viewer')\n const hiddenAt = new Map(rows.map(r => [(r as any).sessionId as string, r.hiddenAt]))\n /* ๋‚ด๊ฐ€ ๋งŒ๋“  ๋Œ€ํ™”์ธ์ง€ ํ‘œ์‹œ โ€” ์‚ญ์ œ ๋ฒ„ํŠผ์„ ๋ณด์ผ์ง€ ๊ฒฐ์ •ํ•˜๋Š” UI ํžŒํŠธ(๊ถŒํ•œ์€ ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ํŒ์ •ํ•œ๋‹ค). */\n const createdByMe = new Set(sessions.filter(s => (s as any).creatorId === user?.id).map(s => s.id))\n const entries = buildInbox(sessions as any, participations as any).map(entry => ({\n ...entry,\n mine: createdByMe.has(entry.id)\n }))\n /* showHidden=true ๋กœ ๋ถˆ๋Ÿฌ **์ „์ฒด**๋ฅผ ์–ป๋Š”๋‹ค โ€” ์ˆจ๊น€ ์—ฌ๋ถ€ ํŒ์ •์„ ๋‘ ๊ณณ์— ๋ณต์ œํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•จ. */\n return applyHidden(entries as any, hiddenAt, true) as any\n }\n\n /**\n * ์„ธ์…˜ ์‚ญ์ œ โ€” **๋งŒ๋“  ์‚ฌ๋žŒ๋งŒ.**\n *\n * ํ˜‘์˜๋Š” ์—ฌ๋Ÿฟ์˜ ๊ธฐ๋ก์ด๊ณ  ์‚ญ์ œ๋Š” ํ™”๋ฉด์—์„œ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ๊ถŒํ•œ์„ ์ขํžŒ๋‹ค(์ด๋ฆ„ ๋ณ€๊ฒฝ์€ ๋˜๋Œ๋ฆด ์ˆ˜\n * ์žˆ์–ด์„œ ๋ˆ„๊ตฌ๋‚˜ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๊ณผ ๋Œ€๋น„). ๊ถŒํ•œ์ด ์—†๋Š” ์‚ฌ๋žŒ์—๊ฒŒ๋Š” `hideAISession`(๋‚ด ๋ชฉ๋ก์—์„œ๋งŒ ์ˆจ๊น€)์ด\n * ์žˆ๋‹ค โ€” ๋‚จ์˜ ๊ธฐ๋ก์„ ์ง€์šฐ์ง€ ์•Š๊ณ  ์ž๊ธฐ ๋ชฉ๋ก์„ ์ •๋ฆฌํ•˜๋Š” ๊ธธ.\n *\n * ์†Œํ”„ํŠธ ์‚ญ์ œ(deletedAt)๋‹ค โ€” ๋ฉ”์‹œ์ง€ยทํŒจ์น˜๋Š” ์„ธ์…˜์„ ํ†ตํ•ด์„œ๋งŒ ๋„๋‹ฌํ•˜๋ฏ€๋กœ ํ•จ๊ป˜ ์‚ฌ๋ผ์ง€๊ณ , ์ž˜๋ชป ์ง€์šด ๊ฒฝ์šฐ\n * ๋ฐ์ดํ„ฐ๋กœ๋Š” ๋‚จ์•„ ์žˆ๋‹ค. ๋‹ค๋ฅธ ์ฐธ์—ฌ์ž ๋ชฉ๋ก์—์„œ๋„ ์‚ฌ๋ผ์ ธ์•ผ ํ•˜๋ฏ€๋กœ meta ํ™œ๋™์œผ๋กœ ๋ฐฉ์†กํ•œ๋‹ค.\n */\n @Mutation(() => Boolean, {\n description:\n '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.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async deleteAISession(@Arg('sessionId') sessionId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, user } = context.state\n const repo = getRepository(ChatSession)\n const session = await repo.findOneBy({ id: sessionId, domain: { id: domain.id } as any })\n if (!session) return false\n if (!user?.id || (session as any).creatorId !== user.id) {\n /* ์กฐ์šฉํ•œ ๋ฌด๋™์ž‘์ด ์•„๋‹ˆ๋ผ ๋ช…์‹œ ์‹คํŒจ โ€” ์ง€์›Œ์ง„ ์ค„ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€๋ฉด ์•ˆ ๋œ๋‹ค. */\n throw new Error('Only the user who created this session may delete it. Hide it from your own list instead.')\n }\n await repo.softDelete(sessionId)\n publishSessionActivity({\n domainId: domain.id,\n sessionId,\n kind: 'meta',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n actorId: user?.id\n })\n return true\n }\n\n /**\n * ๋‚ด ๋ชฉ๋ก์—์„œ ์ˆจ๊ธฐ๊ธฐยท๋˜๋Œ๋ฆฌ๊ธฐ โ€” **๋‚˜์—๊ฒŒ๋งŒ** ์ ์šฉ๋œ๋‹ค(๋Œ€ํ™”ยท๋‹ค๋ฅธ ์ฐธ์—ฌ์ž ๋ชฉ๋ก์€ ๊ทธ๋Œ€๋กœ).\n *\n * ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ์–ด๋„ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค โ€” ๋ชฉ๋ก์—๋Š” ๊ฐ™์€ ๊ณต๊ฐ„์˜ ๋‚จ์˜ ํ˜‘์˜๋„ ๋ณด์ด๊ณ , ๊ทธ๊ฒƒ์„ ์ •๋ฆฌํ•˜๋Š”\n * ๊ฒƒ์€ ๋‚จ์—๊ฒŒ ์•„๋ฌด ์˜ํ–ฅ์ด ์—†๋‹ค. ๊ทธ ๊ฒฝ์šฐ 'viewer' ์—ญํ• ๋กœ ๊ธฐ๋ก๋งŒ ๋‚จ๊ธด๋‹ค(์ฐธ์—ฌ์ž ๋ช…๋ถ€ยท๋ฐฐ์ง€์—๋Š” ๋“ค์ง€ ์•Š๋Š”๋‹ค).\n */\n @Mutation(() => Boolean, {\n description:\n 'Hide (or unhide) a session from the calling user\\'s own list. Per-user and reversible โ€” the session and other participants are unaffected. Activity newer than the hide makes it reappear; the inbox filter (showHidden) reveals hidden ones.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async hideAISession(\n @Arg('sessionId') sessionId: string,\n @Ctx() context: ResolverContext,\n @Arg('hidden', { nullable: true }) hidden?: boolean\n ): Promise<boolean> {\n const { domain, user } = context.state\n if (!user?.id) return false\n const session = await getRepository(ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } as any })\n if (!session) return false\n const repo = getRepository(ChatSessionParticipant)\n const existing = await repo.findOne({\n where: { domain: { id: domain.id } as any, session: { id: sessionId } as any, user: { id: user.id } as any }\n })\n const hiddenAt = hidden === false ? null : new Date()\n if (existing) {\n await repo.update(existing.id!, { hiddenAt, updater: user } as any)\n } else {\n if (hidden === false) return true // ์ˆจ๊ธด ์ ์ด ์—†์œผ๋ฉด ๋˜๋Œ๋ฆด ๊ฒƒ๋„ ์—†๋‹ค\n await repo.save({\n domain: { id: domain.id } as any,\n session: { id: sessionId } as any,\n user: { id: user.id } as any,\n role: 'viewer', // ๋Œ€ํ™”์— ๋‚€ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ˆจ๊น€๋งŒ ๊ธฐ๋กํ•œ๋‹ค\n hiddenAt,\n creator: user,\n updater: user\n } as any)\n }\n return true\n }\n\n /**\n * ์„ธ์…˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ โ€” **์ฐธ์—ฌ์ž ๋ˆ„๊ตฌ๋‚˜** ํ•  ์ˆ˜ ์žˆ๋‹ค.\n *\n * ์™œ ์†Œ์œ ์ž ์ „์šฉ์ด ์•„๋‹Œ๊ฐ€: ์ด๋ฆ„์€ ๋Œ€ํ™”์˜ ์ฃผ์ œํ‘œ์ด๊ณ  ํ˜‘์˜๋Š” ์—ฌ๋Ÿฟ์ด ํ•œ๋‹ค. ๋…ผ์˜๊ฐ€ ํ˜๋Ÿฌ ์ฃผ์ œ๊ฐ€ ๋‹ฌ๋ผ์กŒ๋Š”๋ฐ\n * ๋งŒ๋“  ์‚ฌ๋žŒ๋งŒ ๊ณ ์น  ์ˆ˜ ์žˆ์œผ๋ฉด ๊ทธ ์‚ฌ๋žŒ์ด ์—†์„ ๋•Œ ๋ชฉ๋ก์ด ๊ณ„์† ์–ด๊ธ‹๋‚œ๋‹ค. ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋ณ€๊ฒฝ์ด๊ณ  ๋Œ€ํ™”\n * ๋‚ด์šฉ์— ์˜ํ–ฅ์ด ์—†์œผ๋ฏ€๋กœ ๊ถŒํ•œ์„ ์ขํž ์ด์œ ๊ฐ€ ์—†๋‹ค(๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋Š” ๊ฒƒ๋งŒ ์ขํžŒ๋‹ค).\n *\n * ๋ณ€๊ฒฝ์€ **meta ํ™œ๋™**์œผ๋กœ ๋ฐฉ์†กํ•œ๋‹ค โ€” ๋ชจ๋“  ์ฐธ์—ฌ์ž ๋ชฉ๋ก์— ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์ง€๋งŒ ์•ˆ ์ฝ์Œ ๋ฐฐ์ง€๋Š” ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค\n * (์ด๋ฆ„ ๋ณ€๊ฒฝ์„ \"์ƒˆ ์†Œ์‹\" ์œผ๋กœ ์•Œ๋ฆฌ๋ฉด ์†Œ์Œ์ด๋‹ค).\n */\n @Mutation(() => Boolean, {\n description:\n '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.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async renameAISession(\n @Arg('sessionId') sessionId: string,\n @Arg('name') name: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user } = context.state\n const trimmed = (name ?? '').trim().slice(0, 200)\n if (!trimmed) return false // ๋นˆ ์ด๋ฆ„์œผ๋กœ ์ง€์šฐ์ง€ ์•Š๋Š”๋‹ค โ€” ์ง€์šฐ๋ ค๋ฉด ๋ณ„๋„ ์˜๋„๊ฐ€ ํ•„์š”ํ•˜๋‹ค\n const participant = await getRepository(ChatSessionParticipant).findOne({\n where: { domain: { id: domain.id } as any, session: { id: sessionId } as any, user: { id: user.id } as any }\n })\n if (!participant) return false // ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ๋‚จ์˜ ๋Œ€ํ™” ์ œ๋ชฉ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋‹ค\n const session = await getRepository(ChatSession).findOneBy({ id: sessionId, domain: { id: domain.id } as any })\n if (!session) return false\n await getRepository(ChatSession).update(sessionId, { name: trimmed, updater: user } as any)\n publishSessionActivity({\n domainId: domain.id,\n sessionId,\n kind: 'meta',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n actorId: user?.id\n })\n return true\n }\n\n /**\n * ์ฝ์€ ์ง€์  ๊ฐฑ์‹  โ€” ์ด ์„ธ์…˜์„ ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค. ๋ฉฑ๋“ฑ(์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•ด๋„ ์ตœ์‹  ์‹œ๊ฐ์œผ๋กœ๋งŒ ์ด๋™).\n * ์ ‘์† ํ‘œ์‹œ(lastSeenAt)์™€ ๋ถ„๋ฆฌํ•œ๋‹ค โ€” ์ฐฝ์„ ์—ด์–ด ๋‘๊ณ  ํ™”๋ฉด์„ ์•ˆ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.\n */\n @Mutation(() => Boolean, {\n description:\n \"Mark a session as read up to now for the current user. Idempotent. Distinct from presence (lastSeenAt) โ€” a window can be open without being read.\"\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async markSessionRead(\n @Arg('sessionId') sessionId: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user } = context.state\n const repo = getRepository(ChatSessionParticipant)\n const mine = await repo.findOne({\n where: { domain: { id: domain.id } as any, session: { id: sessionId } as any, user: { id: user.id } as any }\n })\n /* ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์ฝ์Œ ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ์ฐธ์—ฌํ•˜์ง€ ์•Š์€ ๋Œ€ํ™”์— ๋‚ด ์ฝ์Œ ์ง€์ ์„ ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. */\n if (!mine) return false\n await repo.update(mine.id!, { lastReadAt: new Date(), updater: user } as any)\n return true\n }\n\n @Query(() => [User], {\n description:\n 'List users in the current domain for `@` mention popup. board-ai ๊ถŒํ•œ์ด๋ฉด ๋ˆ„๊ตฌ๋‚˜ ๋ฉ˜์…˜์šฉ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅ (๊ด€๋ฆฌ์ž ์ „์šฉ users() ์™€ ๋ณ„๋„).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async domainUsersForMention(\n @Arg('query', { nullable: true, description: 'Substring to match against name/email (case-insensitive). Empty โ†’ all.' }) query: string | undefined,\n @Arg('limit', () => Int, { nullable: true, defaultValue: 50 }) limit: number,\n @Ctx() context: ResolverContext\n ): Promise<User[]> {\n const { domain } = context.state\n const qb = buildDomainUsersQueryBuilder(domain.id, 'USER')\n if (query?.trim()) {\n const q = `%${query.trim().toLowerCase()}%`\n qb.andWhere('(LOWER(USER.name) LIKE :q OR LOWER(USER.email) LIKE :q)', { q })\n }\n qb.orderBy('USER.name', 'ASC').limit(limit)\n return await qb.getMany()\n }\n\n @Query(() => [ChatSessionParticipant], {\n description: 'List participants of a ChatSession (members / owner).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatSessionParticipants(\n @Arg('sessionId') sessionId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSessionParticipant[]> {\n const { domain } = context.state\n const session = await getRepository(ChatSession).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) return []\n return await getRepository(ChatSessionParticipant).find({\n where: { session: { id: sessionId } as any, domain: { id: domain.id } },\n order: { createdAt: 'ASC' }\n })\n }\n\n @Mutation(() => Boolean, {\n description: 'Presence heartbeat โ€” bump current user\\'s lastSeenAt in the session (online indicator).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async touchPresence(@Arg('sessionId') sessionId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, user } = context.state\n const repo = getRepository(ChatSessionParticipant)\n const part = await repo.findOne({\n where: { session: { id: sessionId } as any, user: { id: user.id } as any, domain: { id: domain.id } as any }\n })\n if (!part) return false // ์ฐธ์—ฌ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด heartbeat ๋ฌด์‹œ (์ดˆ๋Œ€ ์ „)\n await repo.update(part.id!, { lastSeenAt: new Date(), updater: user } as any)\n return true\n }\n\n @Query(() => [ChatMessage], { description: 'List chat messages of a session, oldest first.' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatMessages(\n @Arg('sessionId') sessionId: string,\n @Arg('limit', () => Int, { nullable: true, defaultValue: 100 }) limit: number,\n @Arg('offset', () => Int, { nullable: true, defaultValue: 0 }) offset: number,\n @Ctx() context: ResolverContext\n ): Promise<ChatMessage[]> {\n const { domain } = context.state\n const session = await getRepository(ChatSession).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) return []\n return await getRepository(ChatMessage).find({\n where: { session: { id: sessionId } as any },\n // creator: ๊ทธ๋ฃน์ฑ— ๋ฐœ์‹ ์ž ํ‘œ์‹œ(name) + ์ž๊ธฐ ์—ฌ๋ถ€ ํŒ์ •(email). ๋‚ด๋ถ€ ๊ด€๋ฆฌํ‚ค(id)์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.\n relations: { creator: true },\n order: { createdAt: 'ASC' },\n take: limit,\n skip: offset\n })\n }\n\n @Query(() => [PatchEntry], { description: 'List patch entries of a session, newest first.' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"query\")')\n async chatPatches(\n @Arg('sessionId') sessionId: string,\n @Arg('limit', () => Int, { nullable: true, defaultValue: 100 }) limit: number,\n @Ctx() context: ResolverContext\n ): Promise<PatchEntry[]> {\n const { domain } = context.state\n const session = await getRepository(ChatSession).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) return []\n return await getRepository(PatchEntry).find({\n where: { session: { id: sessionId } as any },\n order: { createdAt: 'DESC' },\n take: limit\n })\n }\n\n @Directive('@transaction')\n @Mutation(() => ChatSession, {\n description:\n 'Start (or get existing) AI chat session anchored to a target. Idempotent โ€” returns first existing or creates one. Use anchorType board for board authoring, space for a site (its co-located twins).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async startAISession(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession> {\n const { domain, user, tx } = context.state\n const repo = getRepository(ChatSession, tx)\n\n const existing = await repo.findOneBy(anchorWhere(domain.id, anchorType, anchorId))\n if (existing) return existing\n\n const session = await repo.save({\n domain,\n ...anchorColumns(anchorType, anchorId),\n creator: user,\n updater: user\n } as any)\n // creator ๋ฅผ owner participant ๋กœ ์ž๋™ ๋“ฑ๋ก โ€” ๋‹ค์ž๊ฐ„ ์ฑ„ํŒ… ๊ธฐ๋ฐ˜.\n await getRepository(ChatSessionParticipant, tx).save({\n domain,\n session: { id: session.id } as any,\n user,\n role: 'owner',\n creator: user,\n updater: user\n } as any)\n return session\n }\n\n /** ๋ณด๋“œ ์ „์šฉ ์ง„์ž…์  โ€” ์•ต์ปค ์‹œ์ž‘์˜ ์–‡์€ ๋ž˜ํผ(๊ธฐ์กด ์†Œ๋น„์ž ๋ฌด๋ณ€๊ฒฝ). */\n @Mutation(() => ChatSession, {\n description: 'Start (or get existing) AI chat session for a board. Thin wrapper over startAISession(board).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async startBoardAISession(\n @Arg('boardId') boardId: string,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession> {\n return await this.startAISession('board', boardId, context)\n }\n\n @Directive('@transaction')\n @Mutation(() => ChatSession, {\n description:\n 'Always create a new AI chat session anchored to a target (no idempotent reuse). For multi-session UX โ€” ์ƒˆ ํƒญ ์—ด๊ธฐ.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async createAISession(\n @Arg('anchorType') anchorType: string,\n @Arg('anchorId') anchorId: string,\n @Arg('name', { nullable: true, description: 'Optional name (defaults to auto-generated `์„ธ์…˜ N`).' }) name: string | undefined,\n @Ctx() context: ResolverContext\n ): Promise<ChatSession> {\n const { domain, user, tx } = context.state\n const repo = getRepository(ChatSession, tx)\n\n // ์ด๋ฆ„ ๋ฏธ์ง€์ • ์‹œ ์ž๋™ ์ƒ์„ฑ โ€” **๊ฐ™์€ ์•ต์ปค**์˜ ๊ธฐ์กด ์„ธ์…˜ ์ˆ˜ + 1.\n let finalName = name?.trim()\n if (!finalName) {\n const count = await repo.count({ where: anchorWhere(domain.id, anchorType, anchorId) })\n finalName = `์„ธ์…˜ ${count + 1}`\n }\n\n const session = await repo.save({\n domain,\n ...anchorColumns(anchorType, anchorId),\n creator: user,\n updater: user,\n name: finalName\n } as any)\n // creator ๋ฅผ owner participant ๋กœ ์ž๋™ ๋“ฑ๋ก.\n await getRepository(ChatSessionParticipant, tx).save({\n domain,\n session: { id: session.id } as any,\n user,\n role: 'owner',\n creator: user,\n updater: user\n } as any)\n return session\n }\n\n @Directive('@transaction')\n @Mutation(() => Boolean, {\n description: 'Rename a ChatSession (tab label).'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async renameChatSession(\n @Arg('sessionId') sessionId: string,\n @Arg('name') name: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user, tx } = context.state\n const repo = getRepository(ChatSession, tx)\n const session = await repo.findOneBy({ id: sessionId, domain: { id: domain.id } })\n if (!session) throw new Error(`ChatSession ${sessionId} not found`)\n await repo.update(sessionId, { name: name.trim() || null, updater: user } as any)\n return true\n }\n\n @Directive('@transaction')\n @Mutation(() => Boolean, {\n description:\n 'Append a system note to a session โ€” context that did not come from a participant typing (e.g. a conversation carried over from another surface, an external state change). The AI sees it as part of the shared history.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async appendSessionNote(\n @Arg('sessionId') sessionId: string,\n @Arg('content') content: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user, tx } = context.state\n const session = await getRepository(ChatSession, tx).findOneBy({ id: sessionId, domain: { id: domain.id } })\n if (!session) throw new Error(`ChatSession ${sessionId} not found`)\n /* ํ”„๋กฌํ”„ํŠธ๊ฐ€ ํ•œ ๊ธฐ๋ก์œผ๋กœ ํญ์ฆํ•˜์ง€ ์•Š๋„๋ก ์ž๋ฅธ๋‹ค โ€” ๋งฅ๋ฝ ์ „๋‹ฌ์ด ๋ชฉ์ ์ด๊ณ  ์ „๋ฌธ ๋ณด์กด์ด ์•„๋‹ˆ๋‹ค. */\n const body = (content ?? '').trim().slice(0, 4000)\n if (!body) return false\n\n const previousLast = await getRepository(ChatMessage, tx).findOne({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'DESC' }\n })\n const sysMsg = await getRepository(ChatMessage, tx).save({\n session: { id: session.id } as any,\n role: 'system',\n content: body,\n creator: user,\n updater: user,\n parentMessage: previousLast ? ({ id: previousLast.id } as any) : undefined\n } as any)\n\n /* ํ™œ๋™ ๊ฐฑ์‹  + ๋ฐฉ์†ก โ€” ๋‹ค๋ฅธ ์ฐธ์—ฌ์ž ๋ชฉ๋กยท์•ˆ ์ฝ์Œ๊ณผ ์—ด๋ ค ์žˆ๋Š” ํ™”๋ฉด์˜ transcript ์ผ๊ด€์„ฑ. best-effort. */\n try {\n await getRepository(ChatSession, tx).update(session.id!, activityPatch(body) as any)\n } catch {\n /* ๊ธฐ๋ก ์ž์ฒด๋ฅผ ๋ง‰์ง€ ์•Š๋Š”๋‹ค */\n }\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: body,\n actorId: user?.id\n })\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: sysMsg })\n return true\n }\n\n @Directive('@transaction')\n @Mutation(() => Boolean, {\n description:\n 'Carry a conversation from another surface into this session as its own turns (user / assistant), preserving order. Use when a lightweight exchange is promoted into a persistent discussion โ€” the participants should read it as the conversation continuing, not as a machine dump.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async appendSessionTurns(\n @Arg('sessionId') sessionId: string,\n @Arg('turns', () => GraphQLJSON) turns: { role?: string; content?: string }[],\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, user, tx } = context.state\n const session = await getRepository(ChatSession, tx).findOneBy({ id: sessionId, domain: { id: domain.id } })\n if (!session) throw new Error(`ChatSession ${sessionId} not found`)\n\n /* ์˜ฎ๊ฒจ์˜จ ๋Œ€ํ™”๋Š” **๊ทธ ๋Œ€ํ™”์˜ ๋ฐœ์–ธ ๊ทธ๋Œ€๋กœ** ๋“ค์–ด๊ฐ„๋‹ค โ€” ํ•œ ๋ฉ์–ด๋ฆฌ ๊ธฐ๋ก์œผ๋กœ ๋ญ‰์น˜๋ฉด ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์—†๊ณ \n * (ํ™”๋ฉด ํ•˜๋‚˜๋ฅผ ์žก์•„๋จน๋Š” ์ดํƒค๋ฆญ ๋ฉ์–ด๋ฆฌ), ๋งˆํฌ๋‹ค์šด๋„ ์ฃฝ๋Š”๋‹ค. ๋Œ€์‹  ๊ฐœ์ˆ˜ยท๊ธธ์ด๋Š” ์ œํ•œํ•œ๋‹ค: ๋งฅ๋ฝ ์ „๋‹ฌ์ด\n * ๋ชฉ์ ์ด๊ณ  ์ „๋ฌธ ๋ณด์กด์ด ์•„๋‹ˆ๋‹ค. */\n const rows = (Array.isArray(turns) ? turns : [])\n .map(t => ({\n role: t?.role === 'ai' || t?.role === 'assistant' ? 'assistant' : t?.role === 'user' ? 'user' : 'system',\n content: (t?.content ?? '').trim().slice(0, 4000)\n }))\n .filter(t => t.content)\n .slice(-40)\n if (!rows.length) return false\n\n let previous = await getRepository(ChatMessage, tx).findOne({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'DESC' }\n })\n const saved: ChatMessage[] = []\n for (const row of rows) {\n previous = await getRepository(ChatMessage, tx).save({\n session: { id: session.id } as any,\n role: row.role,\n content: row.content,\n creator: user,\n updater: user,\n parentMessage: previous ? ({ id: previous.id } as any) : undefined\n } as any)\n saved.push(previous!)\n }\n\n /* ํ™œ๋™์€ ๋งˆ์ง€๋ง‰ ๋ฐœ์–ธ ๊ธฐ์ค€ ํ•œ ๋ฒˆ๋งŒ โ€” ์˜ฎ๊ธด ์ค„๋งˆ๋‹ค ๋ชฉ๋ก์„ ํ”๋“ค ์ด์œ ๊ฐ€ ์—†๋‹ค. */\n const last = rows[rows.length - 1]\n try {\n await getRepository(ChatSession, tx).update(session.id!, activityPatch(last.content) as any)\n } catch {\n /* best-effort โ€” ์˜ฎ๊ธฐ๊ธฐ ์ž์ฒด๋ฅผ ๋ง‰์ง€ ์•Š๋Š”๋‹ค */\n }\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: last.content,\n actorId: user?.id\n })\n for (const message of saved) {\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message })\n }\n return true\n }\n\n @Directive('@transaction')\n @Mutation(() => PatchEntry, {\n description: 'Record a patch from user direct edit. Adds a system message so AI sees the change next turn.'\n })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async recordDirectPatch(\n @Arg('sessionId') sessionId: string,\n @Arg('ops', () => GraphQLJSON) ops: any[],\n @Arg('summary', { nullable: true }) summary: string | undefined,\n @Ctx() context: ResolverContext\n ): Promise<PatchEntry> {\n const { domain, tx, user } = context.state\n const session = await getRepository(ChatSession, tx).findOneBy({\n id: sessionId,\n domain: { id: domain.id }\n })\n if (!session) throw new Error(`ChatSession ${sessionId} not found`)\n\n const entry = await getRepository(PatchEntry, tx).save({\n session: { id: session.id } as any,\n source: 'user-direct',\n ops: JSON.stringify(ops || []),\n summary,\n reverted: false\n } as any)\n\n // AI ์ปจํ…์ŠคํŠธ ํ๋ฆ„ โ€” system ๋ฉ”์‹œ์ง€ ์ž๋™ ์ถ”๊ฐ€. creator = ์ง์ ‘ ํŽธ์ง‘ํ•œ ์‚ฌ์šฉ์ž.\n // parentMessage = ์ง์ „ in-session ๋ฉ”์‹œ์ง€ (timeline ์—ฐ์†์„ฑ ์œ ์ง€ โ€” system ๋„ chain ์— ํ•ฉ๋ฅ˜).\n const previousLast = await getRepository(ChatMessage, tx).findOne({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'DESC' }\n })\n const sysMsg = await getRepository(ChatMessage, tx).save({\n session: { id: session.id } as any,\n role: 'system',\n content: `User directly edited the board: ${summary ?? `${(ops || []).length} op(s)`}`,\n relatedPatchId: entry.id,\n creator: user,\n updater: user,\n parentMessage: previousLast ? ({ id: previousLast.id } as any) : undefined\n } as any)\n\n /* ์„ธ์…˜ ํ™œ๋™ ๊ฐฑ์‹  โ€” ์ง์ ‘ ํŽธ์ง‘๋„ ๋‹ค๋ฅธ ์ฐธ์—ฌ์ž์—๊ฒŒ๋Š” ์ƒˆ ์†Œ์‹์ด๋‹ค(๋ชฉ๋ก ์ •๋ ฌยท์•ˆ ์ฝ์Œ ํŒ์ •). */\n try {\n await getRepository(ChatSession, tx).update(session.id!, activityPatch(sysMsg.content) as any)\n } catch {\n /* best-effort โ€” ํŽธ์ง‘ ๊ธฐ๋ก์„ ๋ง‰์ง€ ์•Š๋Š”๋‹ค */\n }\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: sysMsg.content,\n actorId: user?.id\n })\n\n // C1: user-direct ํŽธ์ง‘๋„ ๊ฐ™์€ board ๊ตฌ๋…์ž์—๊ฒŒ ๋ฐฉ์†ก (AIยทํƒ€์ฐธ์—ฌ์ž ์‹ค์‹œ๊ฐ„ ๋ฐ˜์˜). best-effort.\n publishBoardPatch({ domainId: domain.id, boardId: session.boardId, patch: entry })\n // C4: ์ง์ ‘ํŽธ์ง‘ system ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ™์€ ์„ธ์…˜ ๊ตฌ๋…์ž์—๊ฒŒ ๋ฐฉ์†ก (transcript ์‹ค์‹œ๊ฐ„ ์ผ๊ด€์„ฑ). best-effort.\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: sysMsg })\n\n return entry\n }\n\n @Directive('@transaction')\n @Mutation(() => Boolean, { description: 'Mark a patch as reverted (does not undo, only flags).' })\n @Directive('@privilege(category: \"board-ai\", privilege: \"mutation\")')\n async revertPatch(\n @Arg('patchId') patchId: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx } = context.state\n await getRepository(PatchEntry, tx).update(patchId, { reverted: true })\n return true\n }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ export interface GroundingSources {
2
+ /** ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ(ํƒ€์ž… ๋ชฉ๋กยท๊ทœ์น™). */
3
+ systemPrompt?: string;
4
+ /** ๋ณด๋“œ ์ƒํƒœ ์š”์•ฝ ๋“ฑ ๋Œ€ํ™” ์•ž์— ๋ถ™์ธ ๋ฌธ๋งฅ. */
5
+ boardContext?: string;
6
+ /** ์‚ฌ์šฉ์žยท์–ด์‹œ์Šคํ„ดํŠธ ์ด๋ ฅ ํ…์ŠคํŠธ. */
7
+ messages?: string[];
8
+ /** tool_result ๋กœ ํšŒ์‹ ๋œ ์›๋ฌธ(์‹œ๊ฐ„์ˆœ). */
9
+ toolResults?: string[];
10
+ }
11
+ /** ๊ทผ๊ฑฐ์ง‘ํ•ฉ์„ ํ•œ ๋ฉ์–ด๋ฆฌ ๋ฌธ์ž์—ด๋กœ. (id ํ† ํฐ ๋Œ€์กฐ๋งŒ ํ•˜๋ฏ€๋กœ ๊ตฌ๋ถ„์ž๋Š” ๊ฐœํ–‰์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค.) */
12
+ export declare function buildGroundedCorpus(sources: GroundingSources): string;
13
+ /**
14
+ * ๋‹ต์ด ๊ทผ๊ฑฐ ๋ฐ–์˜ ์‹๋ณ„์ž๋ฅผ ์ง€๋ชฉํ–ˆ์œผ๋ฉด ๊ทธ ๋ชฉ๋ก. ๋น„๋ฉด ์ ‘์ง€ ์ •์ƒ.
15
+ * ๋‹ต์„ ๊ณ ์น˜๊ฑฐ๋‚˜ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•  ๊ฒฝ๊ณ ๋งŒ ๋‚ธ๋‹ค.
16
+ */
17
+ export declare function checkReplyGrounding(reply: string, sources: GroundingSources): string[];
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildGroundedCorpus = buildGroundedCorpus;
4
+ exports.checkReplyGrounding = checkReplyGrounding;
5
+ /*
6
+ * ์ ‘์ง€ ๊ฒ€์‚ฌ โ€” ์ด ๋Œ€ํ™”๋ฉด์˜ ๋‹ต์ด **๋ชจ๋ธ์ด ์‹ค์ œ๋กœ ๋ฐ›์€ ๊ฒƒ** ์•ˆ์—์„œ ๋งํ–ˆ๋Š”์ง€.
7
+ *
8
+ * โ”€โ”€ ์™œ board-ai ์— ํ•„์š”ํ•œ๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
9
+ * ํ˜‘์˜ ๋„ํฌ(operato-twin)๋Š” board-ai ์œ„์— ์„œ ์žˆ๊ณ , ์—ฌ๊ธฐ์„œ ์‚ฌ๋žŒ๋“ค์ด ์šด์˜ ์ƒํƒœ๋ฅผ ๋ฌป๋Š”๋‹ค.
10
+ * ํ—ค๋” ๊ฒฝ๋กœ(twin-ai)์—๋Š” ์ ‘์ง€ ๊ฐ€๋“œ๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ ๋„ํฌ ๊ฒฝ๋กœ์—๋Š” ์—†์—ˆ๋‹ค โ€” ๊ฐ™์€ ์งˆ๋ฌธ์— ๋Œ€ํ•ด
11
+ * ํ•œ์ชฝ์€ ๊ฒ€์ฆ๋˜๊ณ  ํ•œ์ชฝ์€ ๊ฒ€์ฆ๋˜์ง€ ์•Š๋Š” ์ƒํƒœ์˜€๋‹ค. ํŒ์ • ๊ทœ์น™์€ ๊ณต์šฉ ๋ชจ๋“ˆ์ด ์†Œ์œ ํ•˜๊ณ 
12
+ * (`@things-factory/ai-client-base` ์˜ grounding), ์ด ํŒŒ์ผ์€ **๋ฌด์—‡์„ ๊ทผ๊ฑฐ๋กœ ๋ณผ ๊ฒƒ์ธ๊ฐ€**๋งŒ ์ •ํ•œ๋‹ค.
13
+ *
14
+ * โ”€โ”€ ๊ทผ๊ฑฐ์ง‘ํ•ฉ์˜ ์ •์˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
15
+ * ๋ชจ๋ธ์ด ์ด ํ„ด์— ๋ฐ›์€ ์ „๋ถ€๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋นผ๋ฉด ์ •์ƒ ๋‹ต์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›๋Š”๋‹ค(์˜คํƒ):
16
+ * ยท systemPrompt โ€” ์ƒ์„ฑ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ํƒ€์ž… ๋ชฉ๋ก ๋“ฑ. ์—ฌ๊ธฐ ์žˆ๋Š” ์ด๋ฆ„์„ ๋งํ•˜๋Š” ๊ฑด ์ •์ƒ์ด๋‹ค.
17
+ * ยท boardContext โ€” ํ˜„์žฌ ๋ณด๋“œ ์š”์•ฝ(์ปดํฌ๋„ŒํŠธ idยท์ด๋ฆ„). ๋ณด๋“œ๋ฅผ ๋‘๊ณ  ๋งํ•˜๋Š” ๊ทผ๊ฑฐ.
18
+ * ยท messages โ€” ์‚ฌ์šฉ์žยท์ด๋ ฅ ํ…์ŠคํŠธ. ์‚ฌ์šฉ์ž๊ฐ€ ๋งํ•œ ์‹๋ณ„์ž๋ฅผ ๋˜๋ฐ›๋Š” ๊ฑด ์ •์ƒ์ด๋‹ค.
19
+ * ยท toolResults โ€” ๋„๊ตฌ๊ฐ€ ๋Œ๋ ค์ค€ ์›๋ฌธ(์••์ถ• ์ „). ์šด์˜ ์ƒํƒœ ๋‹ต์˜ ์œ ์ผํ•œ ์‚ฌ์‹ค ๊ทผ๊ฑฐ.
20
+ *
21
+ * โ”€โ”€ ์‚ฌ์ •๊ฑฐ๋ฆฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
22
+ * ์‹๋ณ„์ž๋งŒ ๋ณธ๋‹ค. "์ง€๊ฒŒ์ฐจ๋“ค์ด ๋ฐ”์˜๊ฒŒ ์›€์ง์ธ๋‹ค" ์ฒ˜๋Ÿผ **์‹๋ณ„์ž ์—†์ด ์ง€์–ด๋‚ธ ์„œ์ˆ **์€ ์žก์ง€ ๋ชปํ•œ๋‹ค โ€”
23
+ * ๊ทธ๊ฑด ๋„๊ตฌ๋ฅผ ๋ถ€๋ฅด์ง€ ์•Š๊ณ  ๋‹ตํ•œ ๊ฒฝ์šฐ์ด๊ณ , ๋„๊ตฌ ํ˜ธ์ถœ์„ ๊ฐ•์ œํ•˜๋Š” ๋ณ„๋„ ๋ ˆ๋ฒ„์˜ ๋ชซ์ด๋‹ค.
24
+ */
25
+ const ai_client_base_1 = require("@things-factory/ai-client-base");
26
+ /** ๊ทผ๊ฑฐ์ง‘ํ•ฉ์„ ํ•œ ๋ฉ์–ด๋ฆฌ ๋ฌธ์ž์—ด๋กœ. (id ํ† ํฐ ๋Œ€์กฐ๋งŒ ํ•˜๋ฏ€๋กœ ๊ตฌ๋ถ„์ž๋Š” ๊ฐœํ–‰์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค.) */
27
+ function buildGroundedCorpus(sources) {
28
+ return [
29
+ sources.systemPrompt ?? '',
30
+ sources.boardContext ?? '',
31
+ ...(sources.messages ?? []),
32
+ ...(sources.toolResults ?? [])
33
+ ]
34
+ .filter(Boolean)
35
+ .join('\n');
36
+ }
37
+ /**
38
+ * ๋‹ต์ด ๊ทผ๊ฑฐ ๋ฐ–์˜ ์‹๋ณ„์ž๋ฅผ ์ง€๋ชฉํ–ˆ์œผ๋ฉด ๊ทธ ๋ชฉ๋ก. ๋น„๋ฉด ์ ‘์ง€ ์ •์ƒ.
39
+ * ๋‹ต์„ ๊ณ ์น˜๊ฑฐ๋‚˜ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•  ๊ฒฝ๊ณ ๋งŒ ๋‚ธ๋‹ค.
40
+ */
41
+ function checkReplyGrounding(reply, sources) {
42
+ if (!reply)
43
+ return [];
44
+ return (0, ai_client_base_1.groundingWarnings)(reply, buildGroundedCorpus(sources));
45
+ }
46
+ //# sourceMappingURL=grounding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grounding.js","sourceRoot":"","sources":["../../server/service/grounding.ts"],"names":[],"mappings":";;AAkCA,kDASC;AAMD,kDAGC;AApDD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,mEAAkE;AAalE,wDAAwD;AACxD,SAAgB,mBAAmB,CAAC,OAAyB;IAC3D,OAAO;QACL,OAAO,CAAC,YAAY,IAAI,EAAE;QAC1B,OAAO,CAAC,YAAY,IAAI,EAAE;QAC1B,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QAC3B,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;KAC/B;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,KAAa,EAAE,OAAyB;IAC1E,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,OAAO,IAAA,kCAAiB,EAAC,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAA;AAC/D,CAAC","sourcesContent":["/*\n * ์ ‘์ง€ ๊ฒ€์‚ฌ โ€” ์ด ๋Œ€ํ™”๋ฉด์˜ ๋‹ต์ด **๋ชจ๋ธ์ด ์‹ค์ œ๋กœ ๋ฐ›์€ ๊ฒƒ** ์•ˆ์—์„œ ๋งํ–ˆ๋Š”์ง€.\n *\n * โ”€โ”€ ์™œ board-ai ์— ํ•„์š”ํ•œ๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํ˜‘์˜ ๋„ํฌ(operato-twin)๋Š” board-ai ์œ„์— ์„œ ์žˆ๊ณ , ์—ฌ๊ธฐ์„œ ์‚ฌ๋žŒ๋“ค์ด ์šด์˜ ์ƒํƒœ๋ฅผ ๋ฌป๋Š”๋‹ค.\n * ํ—ค๋” ๊ฒฝ๋กœ(twin-ai)์—๋Š” ์ ‘์ง€ ๊ฐ€๋“œ๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ ๋„ํฌ ๊ฒฝ๋กœ์—๋Š” ์—†์—ˆ๋‹ค โ€” ๊ฐ™์€ ์งˆ๋ฌธ์— ๋Œ€ํ•ด\n * ํ•œ์ชฝ์€ ๊ฒ€์ฆ๋˜๊ณ  ํ•œ์ชฝ์€ ๊ฒ€์ฆ๋˜์ง€ ์•Š๋Š” ์ƒํƒœ์˜€๋‹ค. ํŒ์ • ๊ทœ์น™์€ ๊ณต์šฉ ๋ชจ๋“ˆ์ด ์†Œ์œ ํ•˜๊ณ \n * (`@things-factory/ai-client-base` ์˜ grounding), ์ด ํŒŒ์ผ์€ **๋ฌด์—‡์„ ๊ทผ๊ฑฐ๋กœ ๋ณผ ๊ฒƒ์ธ๊ฐ€**๋งŒ ์ •ํ•œ๋‹ค.\n *\n * โ”€โ”€ ๊ทผ๊ฑฐ์ง‘ํ•ฉ์˜ ์ •์˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๋ชจ๋ธ์ด ์ด ํ„ด์— ๋ฐ›์€ ์ „๋ถ€๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋นผ๋ฉด ์ •์ƒ ๋‹ต์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›๋Š”๋‹ค(์˜คํƒ):\n * ยท systemPrompt โ€” ์ƒ์„ฑ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ํƒ€์ž… ๋ชฉ๋ก ๋“ฑ. ์—ฌ๊ธฐ ์žˆ๋Š” ์ด๋ฆ„์„ ๋งํ•˜๋Š” ๊ฑด ์ •์ƒ์ด๋‹ค.\n * ยท boardContext โ€” ํ˜„์žฌ ๋ณด๋“œ ์š”์•ฝ(์ปดํฌ๋„ŒํŠธ idยท์ด๋ฆ„). ๋ณด๋“œ๋ฅผ ๋‘๊ณ  ๋งํ•˜๋Š” ๊ทผ๊ฑฐ.\n * ยท messages โ€” ์‚ฌ์šฉ์žยท์ด๋ ฅ ํ…์ŠคํŠธ. ์‚ฌ์šฉ์ž๊ฐ€ ๋งํ•œ ์‹๋ณ„์ž๋ฅผ ๋˜๋ฐ›๋Š” ๊ฑด ์ •์ƒ์ด๋‹ค.\n * ยท toolResults โ€” ๋„๊ตฌ๊ฐ€ ๋Œ๋ ค์ค€ ์›๋ฌธ(์••์ถ• ์ „). ์šด์˜ ์ƒํƒœ ๋‹ต์˜ ์œ ์ผํ•œ ์‚ฌ์‹ค ๊ทผ๊ฑฐ.\n *\n * โ”€โ”€ ์‚ฌ์ •๊ฑฐ๋ฆฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์‹๋ณ„์ž๋งŒ ๋ณธ๋‹ค. \"์ง€๊ฒŒ์ฐจ๋“ค์ด ๋ฐ”์˜๊ฒŒ ์›€์ง์ธ๋‹ค\" ์ฒ˜๋Ÿผ **์‹๋ณ„์ž ์—†์ด ์ง€์–ด๋‚ธ ์„œ์ˆ **์€ ์žก์ง€ ๋ชปํ•œ๋‹ค โ€”\n * ๊ทธ๊ฑด ๋„๊ตฌ๋ฅผ ๋ถ€๋ฅด์ง€ ์•Š๊ณ  ๋‹ตํ•œ ๊ฒฝ์šฐ์ด๊ณ , ๋„๊ตฌ ํ˜ธ์ถœ์„ ๊ฐ•์ œํ•˜๋Š” ๋ณ„๋„ ๋ ˆ๋ฒ„์˜ ๋ชซ์ด๋‹ค.\n */\nimport { groundingWarnings } from '@things-factory/ai-client-base'\n\nexport interface GroundingSources {\n /** ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ(ํƒ€์ž… ๋ชฉ๋กยท๊ทœ์น™). */\n systemPrompt?: string\n /** ๋ณด๋“œ ์ƒํƒœ ์š”์•ฝ ๋“ฑ ๋Œ€ํ™” ์•ž์— ๋ถ™์ธ ๋ฌธ๋งฅ. */\n boardContext?: string\n /** ์‚ฌ์šฉ์žยท์–ด์‹œ์Šคํ„ดํŠธ ์ด๋ ฅ ํ…์ŠคํŠธ. */\n messages?: string[]\n /** tool_result ๋กœ ํšŒ์‹ ๋œ ์›๋ฌธ(์‹œ๊ฐ„์ˆœ). */\n toolResults?: string[]\n}\n\n/** ๊ทผ๊ฑฐ์ง‘ํ•ฉ์„ ํ•œ ๋ฉ์–ด๋ฆฌ ๋ฌธ์ž์—ด๋กœ. (id ํ† ํฐ ๋Œ€์กฐ๋งŒ ํ•˜๋ฏ€๋กœ ๊ตฌ๋ถ„์ž๋Š” ๊ฐœํ–‰์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค.) */\nexport function buildGroundedCorpus(sources: GroundingSources): string {\n return [\n sources.systemPrompt ?? '',\n sources.boardContext ?? '',\n ...(sources.messages ?? []),\n ...(sources.toolResults ?? [])\n ]\n .filter(Boolean)\n .join('\\n')\n}\n\n/**\n * ๋‹ต์ด ๊ทผ๊ฑฐ ๋ฐ–์˜ ์‹๋ณ„์ž๋ฅผ ์ง€๋ชฉํ–ˆ์œผ๋ฉด ๊ทธ ๋ชฉ๋ก. ๋น„๋ฉด ์ ‘์ง€ ์ •์ƒ.\n * ๋‹ต์„ ๊ณ ์น˜๊ฑฐ๋‚˜ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•  ๊ฒฝ๊ณ ๋งŒ ๋‚ธ๋‹ค.\n */\nexport function checkReplyGrounding(reply: string, sources: GroundingSources): string[] {\n if (!reply) return []\n return groundingWarnings(reply, buildGroundedCorpus(sources))\n}\n"]}
@@ -25,6 +25,15 @@ export interface ChatOptions {
25
25
  * ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ํŽธ์ง‘์ด ํ•œ ๋Œ€ํ™”์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.
26
26
  */
27
27
  boardTools?: boolean;
28
+ /**
29
+ * **์ฒซ ํ„ด์— ๋„๊ตฌ ํ˜ธ์ถœ์„ ๊ฐ•์ œ**ํ•œ๋‹ค. ๋ผ์ด๋ธŒ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋Œ€ํ™”๋ฉด(์šด์˜ ํ˜‘์˜)์—์„œ ์ผ ๋‹ค.
30
+ *
31
+ * ์ ‘์ง€ ๊ฐ€๋“œ๋Š” ๋‹ต์ด ์ง€์–ด๋‚ธ **์‹๋ณ„์ž**๋ฅผ ์žก์ง€๋งŒ, ์‹๋ณ„์ž ์—†์ด ์ง€์–ด๋‚ธ **์ƒํ™ฉ ์„œ์ˆ **์€ ์žก์ง€ ๋ชปํ•œ๋‹ค
32
+ * ("์ง€๊ฒŒ์ฐจ๋“ค์ด ๋ฐ”์˜๊ฒŒ ์›€์ง์ด๊ณ  ์žˆ๋‹ค" โ€” ์‹ค์ œ๋กœ ๋‚˜์˜จ ๋‹ต์ด๋‹ค). ๊ทธ๊ฑด ๋„๊ตฌ๋ฅผ ์•„์˜ˆ ๋ถ€๋ฅด์ง€ ์•Š๊ณ  ๋‹ตํ•œ
33
+ * ๊ฒฝ์šฐ์—ฌ์„œ ์‚ฌํ›„ ๊ฒ€์‚ฌ๋กœ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค. ์ฒซ ํ„ด์— ๋ฌด์—‡์ด๋“  ์กฐํšŒํ•˜๊ฒŒ ๋งŒ๋“ค๋ฉด ๊ทผ๊ฑฐ ์—†์ด ์ƒํƒœ๋ฅผ ๋‹จ์–ธํ• 
34
+ * ๊ธธ์ด ์—†์–ด์ง„๋‹ค. ์ผœ๊ณ  ๋„๋Š” ํŒ๋‹จ์€ ๋Œ€ํ™”๋ฉด์ด ํ•œ๋‹ค โ€” ์„œ๋ฒ„๊ฐ€ ์งˆ๋ฌธ ๋‚ด์šฉ์„ ๋ˆˆ์น˜๋กœ ๋ถ„๋ฅ˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.
35
+ */
36
+ requireGroundingTools?: boolean;
28
37
  /**
29
38
  * ํ˜ธ์ŠคํŠธ(๋Œ€ํ™”๋ฉด)๊ฐ€ ์‹ค์–ด ๋ณด๋‚ด๋Š” **๋ฌธ๋งฅ** โ€” ๋„๊ตฌ๊ฐ€ ctx.state.host ๋กœ ์ฝ๋Š”๋‹ค.
30
39
  *
@@ -131,6 +140,13 @@ export interface ChatResponse {
131
140
  followUp?: string;
132
141
  /** AI ๊ฐ€ ์‘๋‹ต ๋งŒ๋“œ๋Š” ๊ณผ์ •์—์„œ ํ˜ธ์ถœํ•œ ๋„๊ตฌ๋“ค (์‹œ๊ฐ„์ˆœ). UI fold-able ๋ฐ•์Šค์šฉ. */
133
142
  toolUsages?: ToolUsage[];
143
+ /**
144
+ * ์ ‘์ง€ ๊ฒฝ๊ณ  โ€” ๋‹ต์ด ์–ธ๊ธ‰ํ–ˆ์œผ๋‚˜ ๋ชจ๋ธ์ด ๋ฐ›์€ ๊ทผ๊ฑฐ(ํ”„๋กฌํ”„ํŠธยท๋ณด๋“œ ๋ฌธ๋งฅยท์ด๋ ฅยท๋„๊ตฌ ๊ฒฐ๊ณผ)์— ์—†๋Š” ์‹๋ณ„์ž.
145
+ *
146
+ * ์žˆ์œผ๋ฉด ๊ทธ ๋Œ€์ƒ์€ **๋งŒ๋“ค์–ด ๋‚ธ ๊ฒƒ์ผ ์ˆ˜ ์žˆ๋‹ค**. ๋‹ต์„ ๋ง‰๊ฑฐ๋‚˜ ๊ณ ์น˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•œ๋‹ค โ€”
147
+ * ํŒ์ • ๊ทœ์น™ยท์‚ฌ์ •๊ฑฐ๋ฆฌ๋Š” ./grounding ์ฐธ์กฐ. ๋น„๋ฉด(undefined) ์ ‘์ง€ ์ •์ƒ.
148
+ */
149
+ groundingWarnings?: string[];
134
150
  }
135
151
  export interface BoardEditPatch {
136
152
  ops: BoardEditOp[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../server/service/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Board AI โ€” ์ž์—ฐ์–ด ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž… ์ •์˜.\n *\n * ํ•ต์‹ฌ ๊ฐœ๋…:\n * - ๋‹จ์ผ ์ง„์ž…์  `chat()`. ์˜๋„ ๋ถ„๋ฅ˜(์ƒ์„ฑ/ํŽธ์ง‘/์Šคํƒ€์ผ/์งˆ๋ฌธ)๋Š” AI ์˜ ์ผ.\n * - ์ถœ๋ ฅ์€ ํ•ญ์ƒ `ChatResponse`. ๋ณด๋“œ ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด `patch` ๋™๋ด‰.\n * - patch ์˜ op ๋Š” ์ง๊ต 4 ์ข… (add / remove / modify / replace).\n * style/move ๋“ฑ์€ modify.patch ์•ˆ์— ํก์ˆ˜.\n */\nimport type { BoardComponent, BoardModel } from '@things-factory/board-import'\n\nexport interface LLMMessage {\n role: 'user' | 'assistant'\n content: string\n}\n\nexport interface ChatOptions {\n /**\n * ์ด ๋Œ€ํ™”์— ๋…ธ์ถœํ•  **์™ธ๋ถ€ ๋„๊ตฌ ์นดํ…Œ๊ณ ๋ฆฌ** ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ(registerToolCategory ์ด๋ฆ„).\n * ๋ฏธ์ง€์ • = ๋“ฑ๋ก๋œ ์ „๋ถ€(ํ˜„ํ–‰ ๋™์ž‘). ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅธ ๋Œ€ํ™”๋ฉด(๋ณด๋“œ ์ €์ž‘ ๋„ํฌ ยท ์šด์˜ ๋„ํฌ)์ด\n * ์„œ๋กœ์˜ ๋Šฅ๋ ฅ์„ ๊ฐ–์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๊ฒฝ๊ณ„ โ€” ์ •์˜๋ฟ ์•„๋‹ˆ๋ผ ์‹คํ–‰ ํŒ์ •์—๋„ ์ ์šฉ๋œ๋‹ค.\n */\n toolCategories?: string[]\n /**\n * ์ฝ”์–ด ๋ณด๋“œ ํŽธ์ง‘ ๋„๊ตฌ(addComponent ยท ์Šคํƒ€์ผ๋ง ยท ์”ฌ ์กฐ์ž‘) ๋…ธ์ถœ ์—ฌ๋ถ€. ๊ธฐ๋ณธ true.\n * ์šด์˜ ๋Œ€ํ™”์ฒ˜๋Ÿผ ๋ณด๋“œ๋ฅผ ๊ณ ์น  ์ด์œ ๊ฐ€ ์—†๋Š” ๋ฉด์—์„œ๋Š” false ๋กœ ๊บผ์„œ, ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋Š” ํ–‰๋™๊ณผ\n * ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ํŽธ์ง‘์ด ํ•œ ๋Œ€ํ™”์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\n boardTools?: boolean\n /**\n * ํ˜ธ์ŠคํŠธ(๋Œ€ํ™”๋ฉด)๊ฐ€ ์‹ค์–ด ๋ณด๋‚ด๋Š” **๋ฌธ๋งฅ** โ€” ๋„๊ตฌ๊ฐ€ ctx.state.host ๋กœ ์ฝ๋Š”๋‹ค.\n *\n * ์™œ ํ•„์š”ํ•œ๊ฐ€: ๋Œ€ํ™”๋ฉด๋งˆ๋‹ค \"์ง€๊ธˆ ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€\" ๊ฐ€ ๋‹ค๋ฅด๋‹ค(์–ด๋А ๊ณต๊ฐ„ยท์–ด๋А ์ธ์Šคํ„ด์Šคยท์–ด๋А ๋ณด๋“œ).\n * ์ด๊ฒƒ ์—†์ด๋Š” ๋„๊ตฌ๊ฐ€ ๋Œ€์ƒ์„ ์•Œ ์ˆ˜ ์—†์–ด ๋ชจ๋ธ์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์‹๋ณ„์ž๋ฅผ ๋˜๋ฌป๋Š”๋‹ค โ€” ์ œํ’ˆ์œผ๋กœ ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š”๋‹ค.\n * ๋ณด๋“œ ๋Œ€ํ™”๋Š” ๋ณด๋“œ ๋ชจ๋ธ์— ๊ฐ์ธํ•ด ์šฐํšŒํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ ๋ณด๋“œ๊ฐ€ ์—†๋Š” ๋Œ€ํ™”๋ฉด(๊ณต๊ฐ„ ํ˜‘์˜)์€ ํ†ต๋กœ๊ฐ€ ์—†์—ˆ๋‹ค.\n *\n * ์‹ ๋ขฐ ๊ฒฝ๊ณ„: ์ด ๊ฐ’์€ **ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ ๊ฒƒ**์ด๋‹ค. ๋„๋ฉ”์ธยท์‚ฌ์šฉ์ž ๊ฐ™์€ ๊ถŒํ•œ ํŒ์ •์— ์“ฐ์ง€ ๋ง ๊ฒƒ\n * (๊ทธ๊ฑด ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜์—์„œ ์–ป๋Š” ctx.state.domain/user ๊ฐ€ ์ •๋ณธ). ์—ฌ๊ธฐ ๊ฐ’์€ \"๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€\" ๋ฒ”์œ„\n * ํžŒํŠธ์ด๋ฉฐ, ๋„๊ตฌ๋Š” ๊ทธ ๋ฒ”์œ„๊ฐ€ ํ˜ธ์ถœ์ž ํ…Œ๋„ŒํŠธ ์†Œ์†์ธ์ง€ ๋ฐ˜๋“œ์‹œ ๊ฒ€์ฆํ•œ๋‹ค.\n */\n hostContext?: any\n /** board-import ์˜ registry scope โ€” ๋“ฑ๋ก๋œ ImportRule ์—์„œ type/category ํ›„๋ณด ์ถ”์ถœ */\n scopes?: string[]\n /** ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๋ฉ”์ธ type (override). ๋ฏธ์ง€์ • ์‹œ scopes ์—์„œ ์ž๋™ ์ถ”์ถœ */\n knownTypes?: string[]\n /** ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์นดํ…Œ๊ณ ๋ฆฌ (override). ๋ฏธ์ง€์ • ์‹œ scopes ์—์„œ ์ž๋™ ์ถ”์ถœ */\n categories?: string[]\n /**\n * ์ปดํฌ๋„ŒํŠธ type ๋ณ„ ์œ ํšจ ์†์„ฑ ์Šคํ‚ด.\n * LLM ์ด ์ •ํ™•ํ•œ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ์„ ํ•˜๋„๋ก type ์˜ description, group, default model ํ‚ค ์ „๋‹ฌ.\n *\n * ์˜ˆ:\n * [{ type: 'rect', description: 'Rectangle', group: 'shape',\n * properties: { fillStyle: '#fff', strokeStyle: '#000' } }, ...]\n */\n componentSchemas?: ComponentSchema[]\n /**\n * ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋ธ๋Ÿฌ์—์„œ ํ˜„์žฌ ์„ ํƒํ•œ ์ปดํฌ๋„ŒํŠธ์˜ `refid` ๋ชฉ๋ก.\n *\n * refid ๋Š” things-scene ์ด ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ์ž๋™ ๋ฐœ๊ธ‰ํ•˜๋Š” universal numeric handle.\n * id (๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์ด๋ฆ„, unique ์•„๋‹˜) ์™€๋Š” ๋‹ค๋ฅธ ๊ฐœ๋… โ€” ์„ ํƒ์€ ํ•ญ์ƒ refid ๋กœ ํ‘œํ˜„.\n */\n selectedRefids?: number[]\n /**\n * popup ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ ์„ ํƒํ•œ #mention ๋งคํ•‘ โ€” {token, refid}.\n * server ๊ฐ€ mention ํ•ด์„ ์‹œ ์ด ๋งคํ•‘์„ ์šฐ์„  ์‚ฌ์šฉ. ๋ณธ๋ฌธ์—” #token ๋งŒ ๋‚จ๊ณ \n * refid ๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜์ง€ ์•Š์Œ (LLM mention map ์—๋งŒ ์ฒจ๋ถ€).\n */\n mentionPicks?: Array<{ token: string; refid: number }>\n /** LLM ๋ชจ๋ธ override */\n model?: string\n /** LLM ์˜ต์…˜ */\n temperature?: number\n maxTokens?: number\n /**\n * Tool ์นดํ…Œ๊ณ ๋ฆฌ (read/external) builder ๊ฐ€ ์‚ฌ์šฉํ•  ์ปจํ…์ŠคํŠธ.\n *\n * board-ai ์ž์ฒด๋Š” ResolverContext / typeorm ์„ ์ง์ ‘ ์•Œ์ง€ ์•Š์ง€๋งŒ, ์™ธ๋ถ€ ๋„๋ฉ”์ธ ํŒจํ‚ค์ง€๊ฐ€\n * ๋“ฑ๋กํ•œ tool (์˜ˆ: board-import ์˜ importBoardAsync) ์€ server-side ์˜์กด์„ฑ (๋„๋ฉ”์ธ,\n * ์‚ฌ์šฉ์ž, ํŠธ๋žœ์žญ์…˜) ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค. board-ai-resolver ๊ฐ€ ResolverContext.state ๋“ฑ์„\n * ์ด ํ•„๋“œ๋กœ ์ „๋‹ฌํ•˜๋ฉด, dispatch ์‹œ closure ๋กœ builder ์˜ ctx.state ์— ์ฃผ์ž….\n *\n * ๋ฏธ์ง€์ •์ด๋ฉด ์™ธ๋ถ€ builder ๋Š” ctx.state === undefined โ€” ๋นˆ ์‘๋‹ต์ด๋‚˜ ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ฑ…์ž„.\n */\n toolCallContext?: any\n}\n\nexport interface ComponentSchema {\n type: string\n description?: string\n group?: string\n /**\n * ์ขŒํ‘œ/ํฌ๊ธฐ/๊ฒฝ๋กœ ๊ด€๋ จ ํ‚ค๋“ค โ€” type ๋งˆ๋‹ค ๋‹ค๋ฆ„.\n * ์˜ˆ) ['left', 'top', 'width', 'height'] vs ['cx', 'cy', 'radius'] vs ['points']\n * LLM ์ด type ๋ณ„ ์ •ํ™•ํ•œ ์ขŒํ‘œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ช…์‹œ.\n */\n geometryKeys?: string[]\n /**\n * ์ขŒํ‘œ ์™ธ type ํŠนํ™” ์†์„ฑ (style, value, min/max ๋“ฑ).\n * default ๊ฐ’ ๋˜๋Š” keys ๋งŒ โ€” LLM ์˜ token ์ ˆ๊ฐ์„ ์œ„ํ•ด ํ˜ธ์ถœ์ž๊ฐ€ ์ ์ ˆํžˆ ์••์ถ•.\n */\n properties?: Record<string, any>\n}\n\n/**\n * AI ๊ฐ€ ์‘๋‹ต์„ ๋งŒ๋“œ๋Š” ๋™์•ˆ ํ˜ธ์ถœํ•œ ๋„๊ตฌ์˜ ์‹œ๊ฐ„์ˆœ trace.\n *\n * UX ๋ชฉ์  โ€” ์‚ฌ์šฉ์ž๊ฐ€ \"์™œ ์ด๋ ‡๊ฒŒ ๋‹ตํ–ˆ์ง€\" ์˜๋ฌธ ๊ฐ€์งˆ ๋•Œ fold-able ๋ฐ•์Šค์—์„œ ํ™•์ธ.\n * ๋””๋ฒ„๊ทธ์šฉ + ์‹ ๋ขฐ๋„ ํ–ฅ์ƒ.\n */\nexport interface ToolUsage {\n /** ๋„๊ตฌ ์ด๋ฆ„ (์˜ˆ: 'getSelection', 'modifyBoard', 'addComponent') */\n name: string\n /** ํ˜ธ์ถœ ์ธ์ž JSON */\n arguments: any\n /** ๋„๊ตฌ ๊ฒฐ๊ณผ โ€” read tool ์€ ์‹ค์ œ ๊ฐ’ (truncated ๊ฐ€๋Šฅ), write tool ์€ queued ๋งˆ์ปค */\n result: any\n /** read tool / write tool ๋ถ„๋ฅ˜ */\n kind: 'read' | 'write' | 'unknown'\n}\n\nexport interface ChatResponse {\n /** ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค„ ํ…์ŠคํŠธ ์‘๋‹ต */\n reply: string\n /** ๋ณด๋“œ ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด patch */\n patch?: BoardEditPatch\n /**\n * Scene ์กฐ์ž‘ action (ephemeral โ€” ๋ชจ๋ธ ๋ณ€๊ฒฝ ์—†์Œ / undo ์˜ํ–ฅ ์—†์Œ).\n * ์‹œ๊ฐ„์ˆœ ์‹œํ€€์Šค๋กœ ํ˜ธ์ŠคํŠธ๊ฐ€ things-scene API ์ง์ ‘ ์‹คํ–‰.\n */\n actions?: BoardActionOp[]\n /** ๋ชจํ˜ธํ•œ ์ž…๋ ฅ ์‹œ ๋ช…ํ™•ํ™” ์งˆ๋ฌธ (patch ์—†์Œ) */\n followUp?: string\n /** AI ๊ฐ€ ์‘๋‹ต ๋งŒ๋“œ๋Š” ๊ณผ์ •์—์„œ ํ˜ธ์ถœํ•œ ๋„๊ตฌ๋“ค (์‹œ๊ฐ„์ˆœ). UI fold-able ๋ฐ•์Šค์šฉ. */\n toolUsages?: ToolUsage[]\n}\n\nexport interface BoardEditPatch {\n ops: BoardEditOp[]\n /** ์‚ฌ์šฉ์ž ๊ฒ€์ˆ˜์šฉ 1-2 ๋ฌธ์žฅ ์š”์•ฝ */\n summary: string\n /** 0..1 ์‹ ๋ขฐ๋„ */\n confidence: number\n}\n\n/**\n * ๋ณด๋“œ ๋ณ€๊ฒฝ op.\n *\n * ์‹๋ณ„์ž ์ •์ฑ… โ€” ๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ ํƒ€๊นƒํŒ…์€ `refid` (number) ๋งŒ ์‚ฌ์šฉ.\n * things-scene ์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” `refid` ๋ฅผ ์ž๋™ ๋ฐœ๊ธ‰๋ฐ›๋Š”๋‹ค (universal).\n * `model.id` ๋Š” optional metadata ์ผ ๋ฟ โ€” ํ•ญ์ƒ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ targeting ์—๋Š”\n * ๋ถ€์ ํ•ฉ. ๋ณ„๊ฐœ ๊ฐœ๋…์ด๋ฏ€๋กœ BoardEditOp / tool ์ธ์ž์—์„œ๋„ ๋ณ„๊ฐœ ์‹๋ณ„์ž๋กœ ๋ถ„๋ฆฌํ•˜์ง€\n * ์•Š๊ณ  refid ๋‹จ์ผ ์ฑ„๋„๋กœ ์ผ์›ํ™”.\n *\n * ๊ณ„์ธต โ€” ๋ณด๋“œ๋Š” **์ตœ์ƒ์œ„ ๋ถ€๋ชจ** (things-scene ์˜ model-layer) ์ด๊ณ  ๊ทธ ์ž์ฒด๋กœ ์ž๊ธฐ\n * ์†์„ฑ์„ ๊ฐ–๋Š”๋‹ค (fillStyle, width, height, fitMode, translate, scale, sky, skyColor,\n * exposure, hemi/dirLight ๊ณ„์—ด, camera ๊ณ„์—ด). ์ž์‹ ์ปดํฌ๋„ŒํŠธ์™€ ๋ณ„๊ฐœ. ๋ณด๋“œ ์†์„ฑ ๋ณ€๊ฒฝ์€\n * `modifyBoard`, ์ž์‹ ๋ณ€๊ฒฝ์€ `modify` (refid ๊ธฐ๋ฐ˜).\n *\n * ์ฃผ์˜ โ€” `name` ์€ ๋ณด๋“œ *์—”ํ‹ฐํ‹ฐ* (DB row) ์˜ ์ปฌ๋Ÿผ์ด์ง€ scene MODEL ์˜ ํ•„๋“œ๊ฐ€ ์•„๋‹ˆ๋‹ค.\n * things-scene ์˜ model-layer ๊ฐ€ ์ธ์‹ ์•ˆ ํ•จ. modifyBoard ๋กœ name ์„ ๋ณด๋‚ด๋ฉด model\n * JSON ์— ์ฃฝ์€ ํ•„๋“œ๋กœ ๋ฐ•ํž ๋ฟ ๋ Œ๋”๋ง/๋™์ž‘์— ์˜ํ–ฅ ์—†์Œ. ๋ณด๋“œ ๋ผ๋ฒจ ๋ณ€๊ฒฝ์€ GraphQL\n * boardPatch (BoardPatch input ์˜ name ํ•„๋“œ) ์˜์—ญ.\n *\n * style ๋ณ€๊ฒฝ / ์ด๋™ / ํฌ๊ธฐ ๋ณ€๊ฒฝ ๋“ฑ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ๋ณ€๊ฒฝ์€ ๋ชจ๋‘ `modify.patch` ์•ˆ์—\n * ํก์ˆ˜. ๋ณด๋“œ root ์†์„ฑ ๋ณ€๊ฒฝ์€ `modifyBoard.patch` ๋กœ.\n */\n/**\n * Phase 2 โ€” Scene ์กฐ์ž‘ op ๋“ค. ๋ชจ๋ธ ์ฐจ์› (์ขŒํ‘œ / ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„ / z-order) ๋ณ€๊ฒฝ์ด์ง€๋งŒ\n * things-scene ์˜ ์ž์ฒด API (align/distribute/group/ungroup/zorder) ๊ฐ€ ์ผ๊ด€๋œ ๊ฒฐ๊ณผ๋ฅผ\n * ๋ณด์žฅํ•˜๋ฏ€๋กœ ํ˜ธ์ŠคํŠธ๊ฐ€ ์ง์ ‘ ํ˜ธ์ถœ. ๋ชจ๋ธ ์ฐจ์› ์‹œ๋ฎฌ๋ ˆ์ด์…˜ (apply-patch) ์€ ๋‹จ์ˆœํ™” โ€”\n * scene ํ˜ธ์ถœ ๊ฒฐ๊ณผ๊ฐ€ ์ •๋ณธ.\n */\nexport type AlignDirection =\n | 'left'\n | 'right'\n | 'center'\n | 'top'\n | 'middle'\n | 'bottom'\n\nexport type DistributeAxis = 'horizontal' | 'vertical'\n\nexport type ZorderDirection = 'front' | 'back' | 'forward' | 'backward'\n\n/**\n * Sugar layout โ€” `arrange` op ์˜ layout ์ข…๋ฅ˜.\n *\n * ์˜๋„: align/distribute ์œ„์— ์–น๋Š” high-level ์˜๋„ ํ‘œํ˜„. AI ๊ฐ€ \"3x2 ๊ทธ๋ฆฌ๋“œ๋กœ\",\n * \"ํ•œ ์ค„๋กœ\", \"์„ธ๋กœ๋กœ ์ผ๋ ฌ\" ๊ฐ™์€ ์ž์—ฐ์–ด๋ฅผ ํ”ฝ์…€ ๋…ธ๊ฐ€๋‹ค ์—†์ด ๋‹จ์ผ op ๋กœ ํ‘œํ˜„.\n *\n * left/top ๋งŒ ๋ณ€๊ฒฝ โ€” width/height ๋Š” ์œ ์ง€. AI ๊ฐ€ ์‚ฌ์ด์ฆˆ๋„ ๋ฐ”๊พธ๋ ค๋ฉด ๋ณ„๋„ modify.\n *\n * ์œ„์น˜ ๊ณ„์‚ฐ์€ ํ˜ธ์ŠคํŠธ (things-scene ์ธก) ๊ฐ€ ๋‹ด๋‹น โ€” ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ ํ˜„์žฌ width/height ๋ฅผ\n * ์ •ํ™•ํžˆ ์•Œ์•„์•ผ ํ•˜๋ฏ€๋กœ model ์ฐจ์› ์‹œ๋ฎฌ๋ ˆ์ด์…˜์€ SCENE_ONLY (apply-patch noop).\n */\nexport type ArrangeLayout =\n | { type: 'grid'; cols: number; gap?: number; anchor?: { left: number; top: number } }\n | {\n type: 'row'\n gap?: number\n anchor?: { left: number; top: number }\n align?: 'start' | 'center' | 'end'\n }\n | {\n type: 'column'\n gap?: number\n anchor?: { left: number; top: number }\n align?: 'start' | 'center' | 'end'\n }\n\n/**\n * C-1 โ€” Scene ์กฐ์ž‘ action ๋“ค (ephemeral). ๋ชจ๋ธ ๋ณ€๊ฒฝ X, ๋”ฐ๋ผ์„œ BoardEditOp ์™€ ๋ณ„๊ฐœ\n * ์ฑ„๋„ (board-action-execute ์ด๋ฒคํŠธ). undo ํžˆ์Šคํ† ๋ฆฌ ๋ฏธ์˜ํ–ฅ, dirty flag ๋ฏธ์˜ํ–ฅ.\n *\n * ์ข…๋ฅ˜:\n * - selectComponents: scene.selected ์ง์ ‘ set (์‚ฌ์šฉ์ž ์„ ํƒ ๋ณ€๊ฒฝ)\n * - centerToComponent: ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋กœ view ์ด๋™\n * - fitToView: ๋ณด๋“œ ์ „์ฒด๊ฐ€ ๋ณด์ด๋„๋ก fit\n * - setSceneMode: edit / view ๋ชจ๋“œ ์ „ํ™˜\n */\nexport type BoardActionOp =\n | { action: 'selectComponents'; refids: number[] }\n | { action: 'centerToComponent'; refid: number; animated?: boolean }\n | { action: 'fitToView'; mode?: 'fit' | 'ratio' | 'width' | 'height' }\n | { action: 'setSceneMode'; mode: 'edit' | 'view' }\n /** ๋‹ค์ค‘ ์ปดํฌ๋„ŒํŠธ outline highlight โ€” search/finder UX ์˜ \"์ด๊ฒŒ ๋ชจ๋‘ ๋งค์นญ์ด๋‹ค\" ์‹œ๊ฐํ™”.\n * things-scene ์˜ highlightSearchResults API ์œ„์ž„ (2D/3D ๋ชจ๋‘ ์ง€์›). */\n | { action: 'highlightComponents'; refids: number[] }\n\nexport type BoardEditOp =\n | { op: 'add'; component: BoardComponent }\n | { op: 'remove'; refid: number }\n | { op: 'modify'; refid: number; patch: Partial<BoardComponent> }\n | { op: 'modifyBoard'; patch: Partial<BoardModel> }\n | { op: 'replace'; board: BoardModel }\n | { op: 'align'; refids: number[]; direction: AlignDirection }\n | { op: 'distribute'; refids: number[]; axis: DistributeAxis }\n | { op: 'group'; refids: number[] }\n | { op: 'ungroup'; refid: number }\n | { op: 'zorder'; refid: number; direction: ZorderDirection }\n | { op: 'arrange'; refids: number[]; layout: ArrangeLayout }\n\n/**\n * BoardAIAssistant โ€” ์ž์—ฐ์–ด ์ฑ„ํŒ…์œผ๋กœ ๋ณด๋“œ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋‹จ์ผ ์ธํ„ฐํŽ˜์ด์Šค.\n *\n * ์‚ฌ์šฉ:\n * const ai = new DefaultBoardAIAssistant(baseClient, { scopes: ['fmsim'] })\n * const r = await ai.chat([{ role: 'user', content: 'AGV 3๋Œ€ ์ถ”๊ฐ€' }], currentBoard)\n * if (r.patch) currentBoard = applyBoardEditPatch(currentBoard, r.patch)\n */\nexport interface BoardAIAssistant {\n readonly id: string\n chat(\n messages: LLMMessage[],\n currentBoard: BoardModel | undefined,\n options?: ChatOptions\n ): Promise<ChatResponse>\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../server/service/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Board AI โ€” ์ž์—ฐ์–ด ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž… ์ •์˜.\n *\n * ํ•ต์‹ฌ ๊ฐœ๋…:\n * - ๋‹จ์ผ ์ง„์ž…์  `chat()`. ์˜๋„ ๋ถ„๋ฅ˜(์ƒ์„ฑ/ํŽธ์ง‘/์Šคํƒ€์ผ/์งˆ๋ฌธ)๋Š” AI ์˜ ์ผ.\n * - ์ถœ๋ ฅ์€ ํ•ญ์ƒ `ChatResponse`. ๋ณด๋“œ ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด `patch` ๋™๋ด‰.\n * - patch ์˜ op ๋Š” ์ง๊ต 4 ์ข… (add / remove / modify / replace).\n * style/move ๋“ฑ์€ modify.patch ์•ˆ์— ํก์ˆ˜.\n */\nimport type { BoardComponent, BoardModel } from '@things-factory/board-import'\n\nexport interface LLMMessage {\n role: 'user' | 'assistant'\n content: string\n}\n\nexport interface ChatOptions {\n /**\n * ์ด ๋Œ€ํ™”์— ๋…ธ์ถœํ•  **์™ธ๋ถ€ ๋„๊ตฌ ์นดํ…Œ๊ณ ๋ฆฌ** ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ(registerToolCategory ์ด๋ฆ„).\n * ๋ฏธ์ง€์ • = ๋“ฑ๋ก๋œ ์ „๋ถ€(ํ˜„ํ–‰ ๋™์ž‘). ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅธ ๋Œ€ํ™”๋ฉด(๋ณด๋“œ ์ €์ž‘ ๋„ํฌ ยท ์šด์˜ ๋„ํฌ)์ด\n * ์„œ๋กœ์˜ ๋Šฅ๋ ฅ์„ ๊ฐ–์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๊ฒฝ๊ณ„ โ€” ์ •์˜๋ฟ ์•„๋‹ˆ๋ผ ์‹คํ–‰ ํŒ์ •์—๋„ ์ ์šฉ๋œ๋‹ค.\n */\n toolCategories?: string[]\n /**\n * ์ฝ”์–ด ๋ณด๋“œ ํŽธ์ง‘ ๋„๊ตฌ(addComponent ยท ์Šคํƒ€์ผ๋ง ยท ์”ฌ ์กฐ์ž‘) ๋…ธ์ถœ ์—ฌ๋ถ€. ๊ธฐ๋ณธ true.\n * ์šด์˜ ๋Œ€ํ™”์ฒ˜๋Ÿผ ๋ณด๋“œ๋ฅผ ๊ณ ์น  ์ด์œ ๊ฐ€ ์—†๋Š” ๋ฉด์—์„œ๋Š” false ๋กœ ๊บผ์„œ, ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋Š” ํ–‰๋™๊ณผ\n * ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ํŽธ์ง‘์ด ํ•œ ๋Œ€ํ™”์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\n boardTools?: boolean\n /**\n * **์ฒซ ํ„ด์— ๋„๊ตฌ ํ˜ธ์ถœ์„ ๊ฐ•์ œ**ํ•œ๋‹ค. ๋ผ์ด๋ธŒ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋Œ€ํ™”๋ฉด(์šด์˜ ํ˜‘์˜)์—์„œ ์ผ ๋‹ค.\n *\n * ์ ‘์ง€ ๊ฐ€๋“œ๋Š” ๋‹ต์ด ์ง€์–ด๋‚ธ **์‹๋ณ„์ž**๋ฅผ ์žก์ง€๋งŒ, ์‹๋ณ„์ž ์—†์ด ์ง€์–ด๋‚ธ **์ƒํ™ฉ ์„œ์ˆ **์€ ์žก์ง€ ๋ชปํ•œ๋‹ค\n * (\"์ง€๊ฒŒ์ฐจ๋“ค์ด ๋ฐ”์˜๊ฒŒ ์›€์ง์ด๊ณ  ์žˆ๋‹ค\" โ€” ์‹ค์ œ๋กœ ๋‚˜์˜จ ๋‹ต์ด๋‹ค). ๊ทธ๊ฑด ๋„๊ตฌ๋ฅผ ์•„์˜ˆ ๋ถ€๋ฅด์ง€ ์•Š๊ณ  ๋‹ตํ•œ\n * ๊ฒฝ์šฐ์—ฌ์„œ ์‚ฌํ›„ ๊ฒ€์‚ฌ๋กœ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค. ์ฒซ ํ„ด์— ๋ฌด์—‡์ด๋“  ์กฐํšŒํ•˜๊ฒŒ ๋งŒ๋“ค๋ฉด ๊ทผ๊ฑฐ ์—†์ด ์ƒํƒœ๋ฅผ ๋‹จ์–ธํ• \n * ๊ธธ์ด ์—†์–ด์ง„๋‹ค. ์ผœ๊ณ  ๋„๋Š” ํŒ๋‹จ์€ ๋Œ€ํ™”๋ฉด์ด ํ•œ๋‹ค โ€” ์„œ๋ฒ„๊ฐ€ ์งˆ๋ฌธ ๋‚ด์šฉ์„ ๋ˆˆ์น˜๋กœ ๋ถ„๋ฅ˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n */\n requireGroundingTools?: boolean\n /**\n * ํ˜ธ์ŠคํŠธ(๋Œ€ํ™”๋ฉด)๊ฐ€ ์‹ค์–ด ๋ณด๋‚ด๋Š” **๋ฌธ๋งฅ** โ€” ๋„๊ตฌ๊ฐ€ ctx.state.host ๋กœ ์ฝ๋Š”๋‹ค.\n *\n * ์™œ ํ•„์š”ํ•œ๊ฐ€: ๋Œ€ํ™”๋ฉด๋งˆ๋‹ค \"์ง€๊ธˆ ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€\" ๊ฐ€ ๋‹ค๋ฅด๋‹ค(์–ด๋А ๊ณต๊ฐ„ยท์–ด๋А ์ธ์Šคํ„ด์Šคยท์–ด๋А ๋ณด๋“œ).\n * ์ด๊ฒƒ ์—†์ด๋Š” ๋„๊ตฌ๊ฐ€ ๋Œ€์ƒ์„ ์•Œ ์ˆ˜ ์—†์–ด ๋ชจ๋ธ์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์‹๋ณ„์ž๋ฅผ ๋˜๋ฌป๋Š”๋‹ค โ€” ์ œํ’ˆ์œผ๋กœ ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š”๋‹ค.\n * ๋ณด๋“œ ๋Œ€ํ™”๋Š” ๋ณด๋“œ ๋ชจ๋ธ์— ๊ฐ์ธํ•ด ์šฐํšŒํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ ๋ณด๋“œ๊ฐ€ ์—†๋Š” ๋Œ€ํ™”๋ฉด(๊ณต๊ฐ„ ํ˜‘์˜)์€ ํ†ต๋กœ๊ฐ€ ์—†์—ˆ๋‹ค.\n *\n * ์‹ ๋ขฐ ๊ฒฝ๊ณ„: ์ด ๊ฐ’์€ **ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ ๊ฒƒ**์ด๋‹ค. ๋„๋ฉ”์ธยท์‚ฌ์šฉ์ž ๊ฐ™์€ ๊ถŒํ•œ ํŒ์ •์— ์“ฐ์ง€ ๋ง ๊ฒƒ\n * (๊ทธ๊ฑด ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜์—์„œ ์–ป๋Š” ctx.state.domain/user ๊ฐ€ ์ •๋ณธ). ์—ฌ๊ธฐ ๊ฐ’์€ \"๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€\" ๋ฒ”์œ„\n * ํžŒํŠธ์ด๋ฉฐ, ๋„๊ตฌ๋Š” ๊ทธ ๋ฒ”์œ„๊ฐ€ ํ˜ธ์ถœ์ž ํ…Œ๋„ŒํŠธ ์†Œ์†์ธ์ง€ ๋ฐ˜๋“œ์‹œ ๊ฒ€์ฆํ•œ๋‹ค.\n */\n hostContext?: any\n /** board-import ์˜ registry scope โ€” ๋“ฑ๋ก๋œ ImportRule ์—์„œ type/category ํ›„๋ณด ์ถ”์ถœ */\n scopes?: string[]\n /** ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๋ฉ”์ธ type (override). ๋ฏธ์ง€์ • ์‹œ scopes ์—์„œ ์ž๋™ ์ถ”์ถœ */\n knownTypes?: string[]\n /** ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์นดํ…Œ๊ณ ๋ฆฌ (override). ๋ฏธ์ง€์ • ์‹œ scopes ์—์„œ ์ž๋™ ์ถ”์ถœ */\n categories?: string[]\n /**\n * ์ปดํฌ๋„ŒํŠธ type ๋ณ„ ์œ ํšจ ์†์„ฑ ์Šคํ‚ด.\n * LLM ์ด ์ •ํ™•ํ•œ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ์„ ํ•˜๋„๋ก type ์˜ description, group, default model ํ‚ค ์ „๋‹ฌ.\n *\n * ์˜ˆ:\n * [{ type: 'rect', description: 'Rectangle', group: 'shape',\n * properties: { fillStyle: '#fff', strokeStyle: '#000' } }, ...]\n */\n componentSchemas?: ComponentSchema[]\n /**\n * ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋ธ๋Ÿฌ์—์„œ ํ˜„์žฌ ์„ ํƒํ•œ ์ปดํฌ๋„ŒํŠธ์˜ `refid` ๋ชฉ๋ก.\n *\n * refid ๋Š” things-scene ์ด ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ์ž๋™ ๋ฐœ๊ธ‰ํ•˜๋Š” universal numeric handle.\n * id (๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์ด๋ฆ„, unique ์•„๋‹˜) ์™€๋Š” ๋‹ค๋ฅธ ๊ฐœ๋… โ€” ์„ ํƒ์€ ํ•ญ์ƒ refid ๋กœ ํ‘œํ˜„.\n */\n selectedRefids?: number[]\n /**\n * popup ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ ์„ ํƒํ•œ #mention ๋งคํ•‘ โ€” {token, refid}.\n * server ๊ฐ€ mention ํ•ด์„ ์‹œ ์ด ๋งคํ•‘์„ ์šฐ์„  ์‚ฌ์šฉ. ๋ณธ๋ฌธ์—” #token ๋งŒ ๋‚จ๊ณ \n * refid ๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜์ง€ ์•Š์Œ (LLM mention map ์—๋งŒ ์ฒจ๋ถ€).\n */\n mentionPicks?: Array<{ token: string; refid: number }>\n /** LLM ๋ชจ๋ธ override */\n model?: string\n /** LLM ์˜ต์…˜ */\n temperature?: number\n maxTokens?: number\n /**\n * Tool ์นดํ…Œ๊ณ ๋ฆฌ (read/external) builder ๊ฐ€ ์‚ฌ์šฉํ•  ์ปจํ…์ŠคํŠธ.\n *\n * board-ai ์ž์ฒด๋Š” ResolverContext / typeorm ์„ ์ง์ ‘ ์•Œ์ง€ ์•Š์ง€๋งŒ, ์™ธ๋ถ€ ๋„๋ฉ”์ธ ํŒจํ‚ค์ง€๊ฐ€\n * ๋“ฑ๋กํ•œ tool (์˜ˆ: board-import ์˜ importBoardAsync) ์€ server-side ์˜์กด์„ฑ (๋„๋ฉ”์ธ,\n * ์‚ฌ์šฉ์ž, ํŠธ๋žœ์žญ์…˜) ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค. board-ai-resolver ๊ฐ€ ResolverContext.state ๋“ฑ์„\n * ์ด ํ•„๋“œ๋กœ ์ „๋‹ฌํ•˜๋ฉด, dispatch ์‹œ closure ๋กœ builder ์˜ ctx.state ์— ์ฃผ์ž….\n *\n * ๋ฏธ์ง€์ •์ด๋ฉด ์™ธ๋ถ€ builder ๋Š” ctx.state === undefined โ€” ๋นˆ ์‘๋‹ต์ด๋‚˜ ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ฑ…์ž„.\n */\n toolCallContext?: any\n}\n\nexport interface ComponentSchema {\n type: string\n description?: string\n group?: string\n /**\n * ์ขŒํ‘œ/ํฌ๊ธฐ/๊ฒฝ๋กœ ๊ด€๋ จ ํ‚ค๋“ค โ€” type ๋งˆ๋‹ค ๋‹ค๋ฆ„.\n * ์˜ˆ) ['left', 'top', 'width', 'height'] vs ['cx', 'cy', 'radius'] vs ['points']\n * LLM ์ด type ๋ณ„ ์ •ํ™•ํ•œ ์ขŒํ‘œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ช…์‹œ.\n */\n geometryKeys?: string[]\n /**\n * ์ขŒํ‘œ ์™ธ type ํŠนํ™” ์†์„ฑ (style, value, min/max ๋“ฑ).\n * default ๊ฐ’ ๋˜๋Š” keys ๋งŒ โ€” LLM ์˜ token ์ ˆ๊ฐ์„ ์œ„ํ•ด ํ˜ธ์ถœ์ž๊ฐ€ ์ ์ ˆํžˆ ์••์ถ•.\n */\n properties?: Record<string, any>\n}\n\n/**\n * AI ๊ฐ€ ์‘๋‹ต์„ ๋งŒ๋“œ๋Š” ๋™์•ˆ ํ˜ธ์ถœํ•œ ๋„๊ตฌ์˜ ์‹œ๊ฐ„์ˆœ trace.\n *\n * UX ๋ชฉ์  โ€” ์‚ฌ์šฉ์ž๊ฐ€ \"์™œ ์ด๋ ‡๊ฒŒ ๋‹ตํ–ˆ์ง€\" ์˜๋ฌธ ๊ฐ€์งˆ ๋•Œ fold-able ๋ฐ•์Šค์—์„œ ํ™•์ธ.\n * ๋””๋ฒ„๊ทธ์šฉ + ์‹ ๋ขฐ๋„ ํ–ฅ์ƒ.\n */\nexport interface ToolUsage {\n /** ๋„๊ตฌ ์ด๋ฆ„ (์˜ˆ: 'getSelection', 'modifyBoard', 'addComponent') */\n name: string\n /** ํ˜ธ์ถœ ์ธ์ž JSON */\n arguments: any\n /** ๋„๊ตฌ ๊ฒฐ๊ณผ โ€” read tool ์€ ์‹ค์ œ ๊ฐ’ (truncated ๊ฐ€๋Šฅ), write tool ์€ queued ๋งˆ์ปค */\n result: any\n /** read tool / write tool ๋ถ„๋ฅ˜ */\n kind: 'read' | 'write' | 'unknown'\n}\n\nexport interface ChatResponse {\n /** ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค„ ํ…์ŠคํŠธ ์‘๋‹ต */\n reply: string\n /** ๋ณด๋“œ ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด patch */\n patch?: BoardEditPatch\n /**\n * Scene ์กฐ์ž‘ action (ephemeral โ€” ๋ชจ๋ธ ๋ณ€๊ฒฝ ์—†์Œ / undo ์˜ํ–ฅ ์—†์Œ).\n * ์‹œ๊ฐ„์ˆœ ์‹œํ€€์Šค๋กœ ํ˜ธ์ŠคํŠธ๊ฐ€ things-scene API ์ง์ ‘ ์‹คํ–‰.\n */\n actions?: BoardActionOp[]\n /** ๋ชจํ˜ธํ•œ ์ž…๋ ฅ ์‹œ ๋ช…ํ™•ํ™” ์งˆ๋ฌธ (patch ์—†์Œ) */\n followUp?: string\n /** AI ๊ฐ€ ์‘๋‹ต ๋งŒ๋“œ๋Š” ๊ณผ์ •์—์„œ ํ˜ธ์ถœํ•œ ๋„๊ตฌ๋“ค (์‹œ๊ฐ„์ˆœ). UI fold-able ๋ฐ•์Šค์šฉ. */\n toolUsages?: ToolUsage[]\n /**\n * ์ ‘์ง€ ๊ฒฝ๊ณ  โ€” ๋‹ต์ด ์–ธ๊ธ‰ํ–ˆ์œผ๋‚˜ ๋ชจ๋ธ์ด ๋ฐ›์€ ๊ทผ๊ฑฐ(ํ”„๋กฌํ”„ํŠธยท๋ณด๋“œ ๋ฌธ๋งฅยท์ด๋ ฅยท๋„๊ตฌ ๊ฒฐ๊ณผ)์— ์—†๋Š” ์‹๋ณ„์ž.\n *\n * ์žˆ์œผ๋ฉด ๊ทธ ๋Œ€์ƒ์€ **๋งŒ๋“ค์–ด ๋‚ธ ๊ฒƒ์ผ ์ˆ˜ ์žˆ๋‹ค**. ๋‹ต์„ ๋ง‰๊ฑฐ๋‚˜ ๊ณ ์น˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•œ๋‹ค โ€”\n * ํŒ์ • ๊ทœ์น™ยท์‚ฌ์ •๊ฑฐ๋ฆฌ๋Š” ./grounding ์ฐธ์กฐ. ๋น„๋ฉด(undefined) ์ ‘์ง€ ์ •์ƒ.\n */\n groundingWarnings?: string[]\n}\n\nexport interface BoardEditPatch {\n ops: BoardEditOp[]\n /** ์‚ฌ์šฉ์ž ๊ฒ€์ˆ˜์šฉ 1-2 ๋ฌธ์žฅ ์š”์•ฝ */\n summary: string\n /** 0..1 ์‹ ๋ขฐ๋„ */\n confidence: number\n}\n\n/**\n * ๋ณด๋“œ ๋ณ€๊ฒฝ op.\n *\n * ์‹๋ณ„์ž ์ •์ฑ… โ€” ๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ ํƒ€๊นƒํŒ…์€ `refid` (number) ๋งŒ ์‚ฌ์šฉ.\n * things-scene ์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” `refid` ๋ฅผ ์ž๋™ ๋ฐœ๊ธ‰๋ฐ›๋Š”๋‹ค (universal).\n * `model.id` ๋Š” optional metadata ์ผ ๋ฟ โ€” ํ•ญ์ƒ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ targeting ์—๋Š”\n * ๋ถ€์ ํ•ฉ. ๋ณ„๊ฐœ ๊ฐœ๋…์ด๋ฏ€๋กœ BoardEditOp / tool ์ธ์ž์—์„œ๋„ ๋ณ„๊ฐœ ์‹๋ณ„์ž๋กœ ๋ถ„๋ฆฌํ•˜์ง€\n * ์•Š๊ณ  refid ๋‹จ์ผ ์ฑ„๋„๋กœ ์ผ์›ํ™”.\n *\n * ๊ณ„์ธต โ€” ๋ณด๋“œ๋Š” **์ตœ์ƒ์œ„ ๋ถ€๋ชจ** (things-scene ์˜ model-layer) ์ด๊ณ  ๊ทธ ์ž์ฒด๋กœ ์ž๊ธฐ\n * ์†์„ฑ์„ ๊ฐ–๋Š”๋‹ค (fillStyle, width, height, fitMode, translate, scale, sky, skyColor,\n * exposure, hemi/dirLight ๊ณ„์—ด, camera ๊ณ„์—ด). ์ž์‹ ์ปดํฌ๋„ŒํŠธ์™€ ๋ณ„๊ฐœ. ๋ณด๋“œ ์†์„ฑ ๋ณ€๊ฒฝ์€\n * `modifyBoard`, ์ž์‹ ๋ณ€๊ฒฝ์€ `modify` (refid ๊ธฐ๋ฐ˜).\n *\n * ์ฃผ์˜ โ€” `name` ์€ ๋ณด๋“œ *์—”ํ‹ฐํ‹ฐ* (DB row) ์˜ ์ปฌ๋Ÿผ์ด์ง€ scene MODEL ์˜ ํ•„๋“œ๊ฐ€ ์•„๋‹ˆ๋‹ค.\n * things-scene ์˜ model-layer ๊ฐ€ ์ธ์‹ ์•ˆ ํ•จ. modifyBoard ๋กœ name ์„ ๋ณด๋‚ด๋ฉด model\n * JSON ์— ์ฃฝ์€ ํ•„๋“œ๋กœ ๋ฐ•ํž ๋ฟ ๋ Œ๋”๋ง/๋™์ž‘์— ์˜ํ–ฅ ์—†์Œ. ๋ณด๋“œ ๋ผ๋ฒจ ๋ณ€๊ฒฝ์€ GraphQL\n * boardPatch (BoardPatch input ์˜ name ํ•„๋“œ) ์˜์—ญ.\n *\n * style ๋ณ€๊ฒฝ / ์ด๋™ / ํฌ๊ธฐ ๋ณ€๊ฒฝ ๋“ฑ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ๋ณ€๊ฒฝ์€ ๋ชจ๋‘ `modify.patch` ์•ˆ์—\n * ํก์ˆ˜. ๋ณด๋“œ root ์†์„ฑ ๋ณ€๊ฒฝ์€ `modifyBoard.patch` ๋กœ.\n */\n/**\n * Phase 2 โ€” Scene ์กฐ์ž‘ op ๋“ค. ๋ชจ๋ธ ์ฐจ์› (์ขŒํ‘œ / ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„ / z-order) ๋ณ€๊ฒฝ์ด์ง€๋งŒ\n * things-scene ์˜ ์ž์ฒด API (align/distribute/group/ungroup/zorder) ๊ฐ€ ์ผ๊ด€๋œ ๊ฒฐ๊ณผ๋ฅผ\n * ๋ณด์žฅํ•˜๋ฏ€๋กœ ํ˜ธ์ŠคํŠธ๊ฐ€ ์ง์ ‘ ํ˜ธ์ถœ. ๋ชจ๋ธ ์ฐจ์› ์‹œ๋ฎฌ๋ ˆ์ด์…˜ (apply-patch) ์€ ๋‹จ์ˆœํ™” โ€”\n * scene ํ˜ธ์ถœ ๊ฒฐ๊ณผ๊ฐ€ ์ •๋ณธ.\n */\nexport type AlignDirection =\n | 'left'\n | 'right'\n | 'center'\n | 'top'\n | 'middle'\n | 'bottom'\n\nexport type DistributeAxis = 'horizontal' | 'vertical'\n\nexport type ZorderDirection = 'front' | 'back' | 'forward' | 'backward'\n\n/**\n * Sugar layout โ€” `arrange` op ์˜ layout ์ข…๋ฅ˜.\n *\n * ์˜๋„: align/distribute ์œ„์— ์–น๋Š” high-level ์˜๋„ ํ‘œํ˜„. AI ๊ฐ€ \"3x2 ๊ทธ๋ฆฌ๋“œ๋กœ\",\n * \"ํ•œ ์ค„๋กœ\", \"์„ธ๋กœ๋กœ ์ผ๋ ฌ\" ๊ฐ™์€ ์ž์—ฐ์–ด๋ฅผ ํ”ฝ์…€ ๋…ธ๊ฐ€๋‹ค ์—†์ด ๋‹จ์ผ op ๋กœ ํ‘œํ˜„.\n *\n * left/top ๋งŒ ๋ณ€๊ฒฝ โ€” width/height ๋Š” ์œ ์ง€. AI ๊ฐ€ ์‚ฌ์ด์ฆˆ๋„ ๋ฐ”๊พธ๋ ค๋ฉด ๋ณ„๋„ modify.\n *\n * ์œ„์น˜ ๊ณ„์‚ฐ์€ ํ˜ธ์ŠคํŠธ (things-scene ์ธก) ๊ฐ€ ๋‹ด๋‹น โ€” ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ ํ˜„์žฌ width/height ๋ฅผ\n * ์ •ํ™•ํžˆ ์•Œ์•„์•ผ ํ•˜๋ฏ€๋กœ model ์ฐจ์› ์‹œ๋ฎฌ๋ ˆ์ด์…˜์€ SCENE_ONLY (apply-patch noop).\n */\nexport type ArrangeLayout =\n | { type: 'grid'; cols: number; gap?: number; anchor?: { left: number; top: number } }\n | {\n type: 'row'\n gap?: number\n anchor?: { left: number; top: number }\n align?: 'start' | 'center' | 'end'\n }\n | {\n type: 'column'\n gap?: number\n anchor?: { left: number; top: number }\n align?: 'start' | 'center' | 'end'\n }\n\n/**\n * C-1 โ€” Scene ์กฐ์ž‘ action ๋“ค (ephemeral). ๋ชจ๋ธ ๋ณ€๊ฒฝ X, ๋”ฐ๋ผ์„œ BoardEditOp ์™€ ๋ณ„๊ฐœ\n * ์ฑ„๋„ (board-action-execute ์ด๋ฒคํŠธ). undo ํžˆ์Šคํ† ๋ฆฌ ๋ฏธ์˜ํ–ฅ, dirty flag ๋ฏธ์˜ํ–ฅ.\n *\n * ์ข…๋ฅ˜:\n * - selectComponents: scene.selected ์ง์ ‘ set (์‚ฌ์šฉ์ž ์„ ํƒ ๋ณ€๊ฒฝ)\n * - centerToComponent: ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋กœ view ์ด๋™\n * - fitToView: ๋ณด๋“œ ์ „์ฒด๊ฐ€ ๋ณด์ด๋„๋ก fit\n * - setSceneMode: edit / view ๋ชจ๋“œ ์ „ํ™˜\n */\nexport type BoardActionOp =\n | { action: 'selectComponents'; refids: number[] }\n | { action: 'centerToComponent'; refid: number; animated?: boolean }\n | { action: 'fitToView'; mode?: 'fit' | 'ratio' | 'width' | 'height' }\n | { action: 'setSceneMode'; mode: 'edit' | 'view' }\n /** ๋‹ค์ค‘ ์ปดํฌ๋„ŒํŠธ outline highlight โ€” search/finder UX ์˜ \"์ด๊ฒŒ ๋ชจ๋‘ ๋งค์นญ์ด๋‹ค\" ์‹œ๊ฐํ™”.\n * things-scene ์˜ highlightSearchResults API ์œ„์ž„ (2D/3D ๋ชจ๋‘ ์ง€์›). */\n | { action: 'highlightComponents'; refids: number[] }\n\nexport type BoardEditOp =\n | { op: 'add'; component: BoardComponent }\n | { op: 'remove'; refid: number }\n | { op: 'modify'; refid: number; patch: Partial<BoardComponent> }\n | { op: 'modifyBoard'; patch: Partial<BoardModel> }\n | { op: 'replace'; board: BoardModel }\n | { op: 'align'; refids: number[]; direction: AlignDirection }\n | { op: 'distribute'; refids: number[]; axis: DistributeAxis }\n | { op: 'group'; refids: number[] }\n | { op: 'ungroup'; refid: number }\n | { op: 'zorder'; refid: number; direction: ZorderDirection }\n | { op: 'arrange'; refids: number[]; layout: ArrangeLayout }\n\n/**\n * BoardAIAssistant โ€” ์ž์—ฐ์–ด ์ฑ„ํŒ…์œผ๋กœ ๋ณด๋“œ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋‹จ์ผ ์ธํ„ฐํŽ˜์ด์Šค.\n *\n * ์‚ฌ์šฉ:\n * const ai = new DefaultBoardAIAssistant(baseClient, { scopes: ['fmsim'] })\n * const r = await ai.chat([{ role: 'user', content: 'AGV 3๋Œ€ ์ถ”๊ฐ€' }], currentBoard)\n * if (r.patch) currentBoard = applyBoardEditPatch(currentBoard, r.patch)\n */\nexport interface BoardAIAssistant {\n readonly id: string\n chat(\n messages: LLMMessage[],\n currentBoard: BoardModel | undefined,\n options?: ChatOptions\n ): Promise<ChatResponse>\n}\n"]}