@things-factory/board-ai 10.0.1 → 10.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/components/board-ai-chat.ts +565 -21
- package/client/components/chat-echo-dedup.test.ts +59 -3
- package/client/components/chat-echo-dedup.ts +32 -0
- package/client/components/chat-input-builder.ts +6 -0
- package/dist-client/client/components/board-ai-chat.d.ts +80 -0
- package/dist-client/client/components/board-ai-chat.js +540 -18
- package/dist-client/client/components/board-ai-chat.js.map +1 -1
- package/dist-client/client/components/chat-echo-dedup.d.ts +2 -0
- package/dist-client/client/components/chat-echo-dedup.js +29 -0
- package/dist-client/client/components/chat-echo-dedup.js.map +1 -1
- package/dist-client/client/components/chat-echo-dedup.test.js +53 -3
- package/dist-client/client/components/chat-echo-dedup.test.js.map +1 -1
- package/dist-client/client/components/chat-input-builder.d.ts +5 -0
- package/dist-client/client/components/chat-input-builder.js +1 -0
- package/dist-client/client/components/chat-input-builder.js.map +1 -1
- package/dist-client/server/service/agentic-loop.d.ts +33 -0
- package/dist-client/server/service/agentic-loop.js +80 -10
- package/dist-client/server/service/agentic-loop.js.map +1 -1
- package/dist-client/server/service/assistant.js +32 -5
- package/dist-client/server/service/assistant.js.map +1 -1
- package/dist-client/server/service/grounding.d.ts +17 -0
- package/dist-client/server/service/grounding.js +42 -0
- package/dist-client/server/service/grounding.js.map +1 -0
- package/dist-client/server/service/types.d.ts +39 -0
- package/dist-client/server/service/types.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/agentic-loop.d.ts +33 -0
- package/dist-server/service/agentic-loop.js +81 -10
- package/dist-server/service/agentic-loop.js.map +1 -1
- package/dist-server/service/assistant.js +31 -4
- package/dist-server/service/assistant.js.map +1 -1
- package/dist-server/service/board-ai-resolver.d.ts +15 -0
- package/dist-server/service/board-ai-resolver.js +121 -2
- package/dist-server/service/board-ai-resolver.js.map +1 -1
- package/dist-server/service/chat-message/chat-message.d.ts +12 -0
- package/dist-server/service/chat-message/chat-message.js +23 -0
- package/dist-server/service/chat-message/chat-message.js.map +1 -1
- package/dist-server/service/chat-message/fold-history.d.ts +30 -0
- package/dist-server/service/chat-message/fold-history.js +29 -0
- package/dist-server/service/chat-message/fold-history.js.map +1 -0
- package/dist-server/service/chat-message/history-summary.d.ts +43 -0
- package/dist-server/service/chat-message/history-summary.js +77 -0
- package/dist-server/service/chat-message/history-summary.js.map +1 -0
- package/dist-server/service/chat-message/llm-history.d.ts +19 -0
- package/dist-server/service/chat-message/llm-history.js +31 -1
- package/dist-server/service/chat-message/llm-history.js.map +1 -1
- package/dist-server/service/chat-session/chat-session.d.ts +8 -0
- package/dist-server/service/chat-session/chat-session.js +5 -0
- package/dist-server/service/chat-session/chat-session.js.map +1 -1
- package/dist-server/service/chat-session/session-inbox.d.ts +26 -0
- package/dist-server/service/chat-session/session-inbox.js +41 -0
- package/dist-server/service/chat-session/session-inbox.js.map +1 -1
- package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +11 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js +17 -1
- package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -1
- package/dist-server/service/chat-session-resolver.d.ts +44 -1
- package/dist-server/service/chat-session-resolver.js +306 -6
- package/dist-server/service/chat-session-resolver.js.map +1 -1
- package/dist-server/service/grounding.d.ts +17 -0
- package/dist-server/service/grounding.js +46 -0
- package/dist-server/service/grounding.js.map +1 -0
- package/dist-server/service/types.d.ts +39 -0
- package/dist-server/service/types.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/server/service/agentic-loop.test.ts +154 -0
- package/server/service/agentic-loop.ts +108 -10
- package/server/service/assistant.ts +36 -5
- package/server/service/board-ai-resolver.ts +131 -2
- package/server/service/chat-message/chat-message.ts +26 -0
- package/server/service/chat-message/fold-history.test.ts +98 -0
- package/server/service/chat-message/fold-history.ts +60 -0
- package/server/service/chat-message/history-summary.test.ts +127 -0
- package/server/service/chat-message/history-summary.ts +100 -0
- package/server/service/chat-message/llm-history.test.ts +65 -0
- package/server/service/chat-message/llm-history.ts +48 -1
- package/server/service/chat-session/chat-session.ts +11 -0
- package/server/service/chat-session/session-inbox.test.ts +69 -1
- package/server/service/chat-session/session-inbox.ts +45 -0
- package/server/service/chat-session-participant/chat-session-participant.ts +14 -0
- package/server/service/chat-session-resolver.ts +297 -5
- package/server/service/dock-contract.test.ts +305 -0
- package/server/service/grounding.test.ts +55 -0
- package/server/service/grounding.ts +53 -0
- package/server/service/types.ts +39 -0
- package/translations/en.json +16 -1
- package/translations/ja.json +16 -1
- package/translations/ko.json +15 -0
- package/translations/ms.json +16 -1
- package/translations/zh.json +16 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-session-resolver.js","sourceRoot":"","sources":["../../server/service/chat-session-resolver.ts"],"names":[],"mappings":";;;;AAAA;;;;;GAKG;AACH,+CAAyG;AACzG,qDAA6C;AAC7C,yDAAgD;AAChD,yGAA8G;AAE9G,iDAAqD;AACrD,qCAA4B;AAE5B,oEAA4D;AAC5D,wEAA6E;AAC7E,sEAA2E;AAC3E,4FAAmF;AACnF,oEAA4D;AAC5D,wGAA+F;AAC/F,iEAAyD;AACzD,2FAA6E;AAC7E,oFAA2E;AAM3E,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CA8B1B,CAAA;AA5BC;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;;iDACvD;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;mDAClE;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2EAA2E,EAAE,CAAC;;yDACjG;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;uDACpD;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;;sDACxE;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oEAAoE,EAAE,CAAC;sCAC7F,IAAI;4DAAA;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;iEAClD;AAG3B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;sCAC1D,IAAI;wDAAA;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;qDAClE;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6FAA6F,EAAE,CAAC;;0DACjG;AA7BjB,qBAAqB;IAJ1B,IAAA,yBAAU,EAAC;QACV,WAAW,EACT,mJAAmJ;KACtJ,CAAC;GACI,qBAAqB,CA8B1B;AAGM,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAGxB,AAAN,KAAK,CAAC,WAAW,CACJ,EAAU,EACd,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,OAAO,CAAC,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAA;IAChG,CAAC;IAIK,AAAN,KAAK,CAAC,kBAAkB,CACN,OAAe,EACxB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,OAAO,CACL,CAAC,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAC7F,CAAA;IACH,CAAC;IAOK,AAAN,KAAK,CAAC,oBAAoB,CACL,UAAkB,EACpB,QAAgB,EAC1B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,OAAO,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,IAAI,CAAC;YAC3C,KAAK,EAAE,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC;YACnD,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC,CAAA;IACJ,CAAC;IAED,4CAA4C;IAKtC,AAAN,KAAK,CAAC,mBAAmB,CACP,OAAe,EACxB,OAAwB;QAE/B,OAAO,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACnE,CAAC;IAED;;;;OAIG;IAMG,AAAN,KAAK,CAAC,gBAAgB,CACD,UAAkB,EACpB,QAAgB,EAC1B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,IAAI,CAAC;YACrD,KAAK,EAAE,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC;SACpD,CAAC,CAAA;QACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG,MAAM,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAC,IAAI,CAAC;YAC5D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,EAAS,EAAE;SAChI,CAAC,CAAA;QACF,OAAO,IAAA,6BAAU,EAAC,QAAe,EAAE,IAAW,CAAQ,CAAA;IACxD,CAAC;IAED;;;;;;;;;OASG;IAMG,AAAN,KAAK,CAAC,eAAe,CACD,SAAiB,EACtB,IAAY,EAClB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACjD,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAA,CAAC,oCAAoC;QAC/D,MAAM,WAAW,GAAG,MAAM,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAC,OAAO,CAAC;YACtE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA,CAAC,6BAA6B;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,CAAC,CAAA;QAC/G,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAA;QAC1B,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QAC3F,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS;YACT,IAAI,EAAE,MAAM;YACZ,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IAMG,AAAN,KAAK,CAAC,eAAe,CACD,SAAiB,EAC5B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;YAC9B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,6DAA6D;QAC7D,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA;QACvB,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;IAOK,AAAN,KAAK,CAAC,qBAAqB,CACgG,KAAyB,EACnF,KAAa,EACrE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,EAAE,GAAG,IAAA,kDAA4B,EAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC1D,IAAI,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;YAClB,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,CAAA;YAC3C,EAAE,CAAC,QAAQ,CAAC,yDAAyD,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;QAC/E,CAAC;QACD,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC3C,OAAO,MAAM,EAAE,CAAC,OAAO,EAAE,CAAA;IAC3B,CAAC;IAMK,AAAN,KAAK,CAAC,uBAAuB,CACT,SAAiB,EAC5B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC;YACzD,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,MAAM,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAC,IAAI,CAAC;YACtD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE;YACvE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC,CAAA;IACJ,CAAC;IAMK,AAAN,KAAK,CAAC,aAAa,CAAmB,SAAiB,EAAS,OAAwB;QACtF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;YAC9B,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA,CAAC,+BAA+B;QACvD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;IAIK,AAAN,KAAK,CAAC,YAAY,CACE,SAAiB,EAC6B,KAAa,EACd,MAAc,EACtE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC;YACzD,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,IAAI,CAAC;YAC3C,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE;YAC5C,qEAAqE;YACrE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;YAC5B,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC3B,IAAI,EAAE,KAAK;YACX,IAAI,EAAE,MAAM;SACb,CAAC,CAAA;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,WAAW,CACG,SAAiB,EAC6B,KAAa,EACtE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC;YACzD,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,MAAM,IAAA,qBAAa,EAAC,2BAAU,CAAC,CAAC,IAAI,CAAC;YAC1C,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE;YAC5C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC5B,IAAI,EAAE,KAAK;SACZ,CAAC,CAAA;IACJ,CAAC;IAQK,AAAN,KAAK,CAAC,cAAc,CACC,UAAkB,EACpB,QAAgB,EAC1B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAA;QAE3C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAA;QACnF,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;QAE7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;YAC9B,MAAM;YACN,GAAG,IAAA,iCAAa,EAAC,UAAU,EAAE,QAAQ,CAAC;YACtC,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;QACT,mDAAmD;QACnD,MAAM,IAAA,qBAAa,EAAC,oDAAsB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACnD,MAAM;YACN,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI;YACJ,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;QACT,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,4CAA4C;IAKtC,AAAN,KAAK,CAAC,mBAAmB,CACP,OAAe,EACxB,OAAwB;QAE/B,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IAC7D,CAAC;IAQK,AAAN,KAAK,CAAC,eAAe,CACA,UAAkB,EACpB,QAAgB,EACmE,IAAwB,EACrH,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAA;QAE3C,2CAA2C;QAC3C,IAAI,SAAS,GAAG,IAAI,EAAE,IAAI,EAAE,CAAA;QAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAA;YACvF,SAAS,GAAG,MAAM,KAAK,GAAG,CAAC,EAAE,CAAA;QAC/B,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;YAC9B,MAAM;YACN,GAAG,IAAA,iCAAa,EAAC,UAAU,EAAE,QAAQ,CAAC;YACtC,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,SAAS;SACT,CAAC,CAAA;QACT,uCAAuC;QACvC,MAAM,IAAA,qBAAa,EAAC,oDAAsB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACnD,MAAM;YACN,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI;YACJ,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;QACT,OAAO,OAAO,CAAA;IAChB,CAAC;IAOK,AAAN,KAAK,CAAC,iBAAiB,CACH,SAAiB,EACtB,IAAY,EAClB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAA;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAClF,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,YAAY,CAAC,CAAA;QACnE,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QACjF,OAAO,IAAI,CAAA;IACb,CAAC;IAOK,AAAN,KAAK,CAAC,iBAAiB,CACH,SAAiB,EACJ,GAAU,EACL,OAA2B,EACxD,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC;YAC7D,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,YAAY,CAAC,CAAA;QAEnE,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,2BAAU,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACrD,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,MAAM,EAAE,aAAa;YACrB,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC;YAC9B,OAAO;YACP,QAAQ,EAAE,KAAK;SACT,CAAC,CAAA;QAET,uDAAuD;QACvD,6EAA6E;QAC7E,MAAM,YAAY,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;YAChE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS,EAAE;YAC7C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SAC7B,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACvD,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,mCAAmC,OAAO,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,MAAM,QAAQ,EAAE;YACtF,cAAc,EAAE,KAAK,CAAC,EAAE;YACxB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,aAAa,EAAE,YAAY,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAU,CAAC,CAAC,CAAC,SAAS;SACpE,CAAC,CAAA;QAET,wDAAwD;QACxD,IAAI,CAAC;YACH,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAG,EAAE,IAAA,gCAAa,EAAC,MAAM,CAAC,OAAO,CAAQ,CAAC,CAAA;QAChG,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;QACD,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS,EAAE,OAAO,CAAC,EAAG;YACtB,IAAI,EAAE,SAAS;YACf,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QAEF,uEAAuE;QACvE,IAAA,+CAAiB,EAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QAClF,yEAAyE;QACzE,IAAA,4CAAkB,EAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAEnF,OAAO,KAAK,CAAA;IACd,CAAC;IAKK,AAAN,KAAK,CAAC,WAAW,CACC,OAAe,EACxB,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC5B,MAAM,IAAA,qBAAa,EAAC,2BAAU,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QACvE,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAA;AAzaY,kDAAmB;AAGxB;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IACvF,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IACT,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAIP;AAIK;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gGAAgG,EAAE,CAAC;IAC3J,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;6DAMP;AAOK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE;QAC1B,WAAW,EACT,mQAAmQ;KACtQ,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+DAOP;AAOK;IAJL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE;QAC1B,WAAW,EAAE,sGAAsG;KACpH,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8DAGP;AAYK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,qBAAqB,CAAC,EAAE;QACpC,WAAW,EACT,sNAAsN;KACzN,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;2DAWP;AAiBK;IALL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,kOAAkO;KACrO,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IACX,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAqBP;AAWK;IALL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,mJAAmJ;KACtJ,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAWP;AAOK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,gBAAI,CAAC,EAAE;QACnB,WAAW,EACT,4GAA4G;KAC/G,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wEAAwE,EAAE,CAAC,CAAA;IACvH,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAA;IAC7D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gEAUP;AAMK;IAJL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,oDAAsB,CAAC,EAAE;QACrC,WAAW,EAAE,uDAAuD;KACrE,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kEAYP;AAMK;IAJL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EAAE,yFAAyF;KACvG,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAChD,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;wDAS9D;AAIK;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;IAC7F,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9D,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAA;IAC7D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;uDAgBP;AAIK;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,2BAAU,CAAC,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;IAC5F,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAaP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE;QAC3B,WAAW,EACT,sMAAsM;KACzM,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAwBP;AAOK;IAJL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE;QAC3B,WAAW,EAAE,+FAA+F;KAC7G,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8DAGP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE;QAC3B,WAAW,EACT,gHAAgH;KACnH,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAA;IAClG,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DA6BP;AAOK;IALL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EAAE,mCAAmC;KACjD,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IACX,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;4DAQP;AAOK;IALL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,2BAAU,EAAE;QAC1B,WAAW,EAAE,8FAA8F;KAC5G,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,KAAK,EAAE,GAAG,EAAE,CAAC,6BAAW,CAAC,CAAA;IAC7B,mBAAA,IAAA,kBAAG,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAClC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;4DAuDP;AAKK;IAHL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;IACjG,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAKP;8BAxaU,mBAAmB;IAD/B,IAAA,uBAAQ,GAAE;GACE,mBAAmB,CAya/B","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":";;;;AAAA;;;;;GAKG;AACH,+CAAyG;AACzG,qDAA6C;AAC7C,yDAAgD;AAChD,yGAA8G;AAE9G,iDAAqD;AACrD,qCAA4B;AAE5B,oEAA4D;AAC5D,wEAA6E;AAC7E,sEAAqH;AACrH,4FAAmF;AACnF,oEAA4D;AAC5D,wGAA+F;AAC/F,iEAAyD;AACzD,2FAA6E;AAC7E,oFAA2E;AAM3E,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CAuC1B,CAAA;AArCC;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;;iDACvD;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;mDAClE;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2EAA2E,EAAE,CAAC;;yDACjG;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;uDACpD;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;;sDACxE;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oEAAoE,EAAE,CAAC;sCAC7F,IAAI;4DAAA;AAGpB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;iEAClD;AAG3B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;sCAC1D,IAAI;wDAAA;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;qDAClE;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6FAA6F,EAAE,CAAC;;0DACjG;AAKrB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,2EAA2E,EAAE,CAAC;;mDACtF;AAId;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gGAAgG,EAAE,CAAC;;qDACzH;AAtCZ,qBAAqB;IAJ1B,IAAA,yBAAU,EAAC;QACV,WAAW,EACT,mJAAmJ;KACtJ,CAAC;GACI,qBAAqB,CAuC1B;AAGM,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAGxB,AAAN,KAAK,CAAC,WAAW,CACJ,EAAU,EACd,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,OAAO,CAAC,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAA;IAChG,CAAC;IAIK,AAAN,KAAK,CAAC,kBAAkB,CACN,OAAe,EACxB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,OAAO,CACL,CAAC,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAC7F,CAAA;IACH,CAAC;IAOK,AAAN,KAAK,CAAC,oBAAoB,CACL,UAAkB,EACpB,QAAgB,EAC1B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,OAAO,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,IAAI,CAAC;YAC3C,KAAK,EAAE,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC;YACnD,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC,CAAA;IACJ,CAAC;IAED,4CAA4C;IAKtC,AAAN,KAAK,CAAC,mBAAmB,CACP,OAAe,EACxB,OAAwB;QAE/B,OAAO,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACnE,CAAC;IAED;;;;OAIG;IAMG,AAAN,KAAK,CAAC,gBAAgB,CACD,UAAkB,EACpB,QAAgB,EAC1B,OAAwB,EAEQ,UAAoB,EAExB,MAAe,EACb,QAAkB,EACV,KAAc,EACb,MAAe;QAE7D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC3G,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAA;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;QACpD,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAQ,CAAA;IACjD,CAAC;IAED;;;;;OAKG;IAMG,AAAN,KAAK,CAAC,qBAAqB,CACN,UAAkB,EACpB,QAAgB,EAC1B,OAAwB,EACQ,UAAoB,EACxB,MAAe,EACb,QAAkB;QAEvD,OAAO,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAA;IAC5G,CAAC;IAED,2EAA2E;IACnE,KAAK,CAAC,cAAc,CAC1B,UAAkB,EAClB,QAAgB,EAChB,OAAwB,EACxB,IAAmE;QAEnE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QACnE,IAAI,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAChE,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAE,CAAS,CAAC,IAAI,CAAC,CAAA;QACjE,OAAO,IAAA,8BAAW,EAAC,OAAc,EAAE,IAAI,CAAC,MAAM,CAAQ,CAAA;IACxD,CAAC;IAED;;;;;OAKG;IAMG,AAAN,KAAK,CAAC,kBAAkB,CACH,UAAkB,EACpB,QAAgB,EAC1B,OAAwB;QAE/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QACnE,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAA;IACzC,CAAC;IAED,kEAAkE;IAC1D,KAAK,CAAC,aAAa,CACzB,UAAkB,EAClB,QAAgB,EAChB,OAAwB;QAExB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC;uEAC+D;QAC/D,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,IAAI,CAAC;YACrD,KAAK,EAAE,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC;YACnD,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE;YAChC,IAAI,EAAE,iCAAc;SACrB,CAAC,CAAA;QACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG,MAAM,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAC,IAAI,CAAC;YAC5D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAG,CAAC,CAAC,EAAS,EAAE;SAChI,CAAC,CAAA;QACF;+CACuC;QACvC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;QAC5D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAE,CAAS,CAAC,SAAmB,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QACrF,6DAA6D;QAC7D,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAE,CAAS,CAAC,SAAS,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACnG,MAAM,OAAO,GAAG,IAAA,6BAAU,EAAC,QAAe,EAAE,cAAqB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC/E,GAAG,KAAK;YACR,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;SAChC,CAAC,CAAC,CAAA;QACH,mEAAmE;QACnE,OAAO,IAAA,8BAAW,EAAC,OAAc,EAAE,QAAQ,EAAE,IAAI,CAAQ,CAAA;IAC3D,CAAC;IAED;;;;;;;;;OASG;IAMG,AAAN,KAAK,CAAC,eAAe,CAAmB,SAAiB,EAAS,OAAwB;QACxF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,CAAC,CAAA;QACzF,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAA;QAC1B,IAAI,CAAC,IAAI,EAAE,EAAE,IAAK,OAAe,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YACxD,8CAA8C;YAC9C,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAA;QAC9G,CAAC;QACD,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;QAChC,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS;YACT,IAAI,EAAE,MAAM;YACZ,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;OAKG;IAMG,AAAN,KAAK,CAAC,aAAa,CACC,SAAiB,EAC5B,OAAwB,EACI,MAAgB;QAEnD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,IAAI,CAAC,IAAI,EAAE,EAAE;YAAE,OAAO,KAAK,CAAA;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,CAAC,CAAA;QAC/G,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAA;QAC1B,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAA;QAClD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;YAClC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;QACrD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QACrE,CAAC;aAAM,CAAC;YACN,IAAI,MAAM,KAAK,KAAK;gBAAE,OAAO,IAAI,CAAA,CAAC,sBAAsB;YACxD,MAAM,IAAI,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS;gBAChC,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS;gBACjC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS;gBAC5B,IAAI,EAAE,QAAQ,EAAE,wBAAwB;gBACxC,QAAQ;gBACR,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,IAAI;aACP,CAAC,CAAA;QACX,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;;;;OASG;IAMG,AAAN,KAAK,CAAC,eAAe,CACD,SAAiB,EACtB,IAAY,EAClB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACjD,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAA,CAAC,oCAAoC;QAC/D,MAAM,WAAW,GAAG,MAAM,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAC,OAAO,CAAC;YACtE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA,CAAC,6BAA6B;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,CAAC,CAAA;QAC/G,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAA;QAC1B,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QAC3F,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS;YACT,IAAI,EAAE,MAAM;YACZ,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IAMG,AAAN,KAAK,CAAC,eAAe,CACD,SAAiB,EAC5B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;YAC9B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,6DAA6D;QAC7D,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA;QACvB,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;IAOK,AAAN,KAAK,CAAC,qBAAqB,CACgG,KAAyB,EACnF,KAAa,EACrE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,EAAE,GAAG,IAAA,kDAA4B,EAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC1D,IAAI,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;YAClB,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,CAAA;YAC3C,EAAE,CAAC,QAAQ,CAAC,yDAAyD,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;QAC/E,CAAC;QACD,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC3C,OAAO,MAAM,EAAE,CAAC,OAAO,EAAE,CAAA;IAC3B,CAAC;IAMK,AAAN,KAAK,CAAC,uBAAuB,CACT,SAAiB,EAC5B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC;YACzD,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,MAAM,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAC,IAAI,CAAC;YACtD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE;YACvE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC,CAAA;IACJ,CAAC;IAMK,AAAN,KAAK,CAAC,aAAa,CAAmB,SAAiB,EAAS,OAAwB;QACtF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,oDAAsB,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;YAC9B,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAS,EAAE;SAC7G,CAAC,CAAA;QACF,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA,CAAC,+BAA+B;QACvD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;IAIK,AAAN,KAAK,CAAC,YAAY,CACE,SAAiB,EAC6B,KAAa,EACd,MAAc,EACtE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC;YACzD,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,IAAI,CAAC;YAC3C,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE;YAC5C,qEAAqE;YACrE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;YAC5B,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;YAC3B,IAAI,EAAE,KAAK;YACX,IAAI,EAAE,MAAM;SACb,CAAC,CAAA;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,WAAW,CACG,SAAiB,EAC6B,KAAa,EACtE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC,CAAC,SAAS,CAAC;YACzD,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,MAAM,IAAA,qBAAa,EAAC,2BAAU,CAAC,CAAC,IAAI,CAAC;YAC1C,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAS,EAAE;YAC5C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC5B,IAAI,EAAE,KAAK;SACZ,CAAC,CAAA;IACJ,CAAC;IAQK,AAAN,KAAK,CAAC,cAAc,CACC,UAAkB,EACpB,QAAgB,EAC1B,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAA;QAE3C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAA;QACnF,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;QAE7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;YAC9B,MAAM;YACN,GAAG,IAAA,iCAAa,EAAC,UAAU,EAAE,QAAQ,CAAC;YACtC,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;QACT,mDAAmD;QACnD,MAAM,IAAA,qBAAa,EAAC,oDAAsB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACnD,MAAM;YACN,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI;YACJ,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;QACT,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,4CAA4C;IAKtC,AAAN,KAAK,CAAC,mBAAmB,CACP,OAAe,EACxB,OAAwB;QAE/B,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IAC7D,CAAC;IAQK,AAAN,KAAK,CAAC,eAAe,CACA,UAAkB,EACpB,QAAgB,EACmE,IAAwB,EACrH,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAA;QAE3C,2CAA2C;QAC3C,IAAI,SAAS,GAAG,IAAI,EAAE,IAAI,EAAE,CAAA;QAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAA,+BAAW,EAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAA;YACvF,SAAS,GAAG,MAAM,KAAK,GAAG,CAAC,EAAE,CAAA;QAC/B,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;YAC9B,MAAM;YACN,GAAG,IAAA,iCAAa,EAAC,UAAU,EAAE,QAAQ,CAAC;YACtC,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,SAAS;SACT,CAAC,CAAA;QACT,uCAAuC;QACvC,MAAM,IAAA,qBAAa,EAAC,oDAAsB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACnD,MAAM;YACN,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI;YACJ,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACP,CAAC,CAAA;QACT,OAAO,OAAO,CAAA;IAChB,CAAC;IAOK,AAAN,KAAK,CAAC,iBAAiB,CACH,SAAiB,EACtB,IAAY,EAClB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAA;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAClF,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,YAAY,CAAC,CAAA;QACnE,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QACjF,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,iBAAiB,CACH,SAAiB,EACnB,OAAe,EACxB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAC5G,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,YAAY,CAAC,CAAA;QACnE,yDAAyD;QACzD,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;QAClD,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA;QAEvB,MAAM,YAAY,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;YAChE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS,EAAE;YAC7C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SAC7B,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACvD,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,aAAa,EAAE,YAAY,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAU,CAAC,CAAC,CAAC,SAAS;SACpE,CAAC,CAAA;QAET,yEAAyE;QACzE,IAAI,CAAC;YACH,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAG,EAAE,IAAA,gCAAa,EAAC,IAAI,CAAQ,CAAC,CAAA;QACtF,CAAC;QAAC,MAAM,CAAC;YACP,mBAAmB;QACrB,CAAC;QACD,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS,EAAE,OAAO,CAAC,EAAG;YACtB,IAAI,EAAE,SAAS;YACf,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QACF,IAAA,4CAAkB,EAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QACnF,OAAO,IAAI,CAAA;IACb,CAAC;IAQK,AAAN,KAAK,CAAC,kBAAkB,CACJ,SAAiB,EACF,KAA4C,EACtE,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAC5G,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,YAAY,CAAC,CAAA;QAEnE;;8BAEsB;QACtB,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACT,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,IAAI,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;YACxG,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;SAClD,CAAC,CAAC;aACF,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;aACtB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAA;QACb,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QAE9B,IAAI,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;YAC1D,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS,EAAE;YAC7C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SAC7B,CAAC,CAAA;QACF,MAAM,KAAK,GAAkB,EAAE,CAAA;QAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;gBACnD,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;gBAClC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,IAAI;gBACb,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAU,CAAC,CAAC,CAAC,SAAS;aAC5D,CAAC,CAAA;YACT,KAAK,CAAC,IAAI,CAAC,QAAS,CAAC,CAAA;QACvB,CAAC;QAED,gDAAgD;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAClC,IAAI,CAAC;YACH,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAG,EAAE,IAAA,gCAAa,EAAC,IAAI,CAAC,OAAO,CAAQ,CAAC,CAAA;QAC9F,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;QACD,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS,EAAE,OAAO,CAAC,EAAG;YACtB,IAAI,EAAE,SAAS;YACf,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QACF,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC5B,IAAA,4CAAkB,EAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAC7E,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAOK,AAAN,KAAK,CAAC,iBAAiB,CACH,SAAiB,EACJ,GAAU,EACL,OAA2B,EACxD,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC;YAC7D,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAA;QACF,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,SAAS,YAAY,CAAC,CAAA;QAEnE,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,2BAAU,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACrD,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,MAAM,EAAE,aAAa;YACrB,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC;YAC9B,OAAO;YACP,QAAQ,EAAE,KAAK;SACT,CAAC,CAAA;QAET,uDAAuD;QACvD,6EAA6E;QAC7E,MAAM,YAAY,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;YAChE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS,EAAE;YAC7C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SAC7B,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;YACvD,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAS;YAClC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,mCAAmC,OAAO,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,MAAM,QAAQ,EAAE;YACtF,cAAc,EAAE,KAAK,CAAC,EAAE;YACxB,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;YACb,aAAa,EAAE,YAAY,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAU,CAAC,CAAC,CAAC,SAAS;SACpE,CAAC,CAAA;QAET,wDAAwD;QACxD,IAAI,CAAC;YACH,MAAM,IAAA,qBAAa,EAAC,6BAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAG,EAAE,IAAA,gCAAa,EAAC,MAAM,CAAC,OAAO,CAAQ,CAAC,CAAA;QAChG,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;QACD,IAAA,oDAAsB,EAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,SAAS,EAAE,OAAO,CAAC,EAAG;YACtB,IAAI,EAAE,SAAS;YACf,UAAU,EAAG,OAAe,CAAC,UAAU;YACvC,QAAQ,EAAG,OAAe,CAAC,QAAQ;YACnC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,IAAI,EAAE,EAAE;SAClB,CAAC,CAAA;QAEF,uEAAuE;QACvE,IAAA,+CAAiB,EAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QAClF,yEAAyE;QACzE,IAAA,4CAAkB,EAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAEnF,OAAO,KAAK,CAAA;IACd,CAAC;IAKK,AAAN,KAAK,CAAC,WAAW,CACC,OAAe,EACxB,OAAwB;QAE/B,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC5B,MAAM,IAAA,qBAAa,EAAC,2BAAU,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QACvE,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAA;AApsBY,kDAAmB;AAGxB;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IACvF,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IACT,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAIP;AAIK;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gGAAgG,EAAE,CAAC;IAC3J,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;6DAMP;AAOK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE;QAC1B,WAAW,EACT,mQAAmQ;KACtQ,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+DAOP;AAOK;IAJL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE;QAC1B,WAAW,EAAE,sGAAsG;KACpH,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8DAGP;AAYK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,qBAAqB,CAAC,EAAE;QACpC,WAAW,EACT,8TAA8T;KACjU,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;IAEL,mBAAA,IAAA,kBAAG,EAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAErC,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACjC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACnC,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3C,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;;;;2DAM9C;AAaK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,kBAAG,EAAE;QAChB,WAAW,EACT,4KAA4K;KAC/K,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;IACL,mBAAA,IAAA,kBAAG,EAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACrC,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACjC,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;;;;gEAGrC;AA0BK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,kBAAG,EAAE;QAChB,WAAW,EACT,0NAA0N;KAC7N,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;6DAIP;AAiDK;IALL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,mQAAmQ;KACtQ,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAC9C,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAmBhE;AAaK;IALL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,+OAA+O;KAClP,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,GAAE,CAAA;IACL,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;;;;wDA0BnC;AAiBK;IALL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,kOAAkO;KACrO,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IACX,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAqBP;AAWK;IALL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,mJAAmJ;KACtJ,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAWP;AAOK;IALL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,gBAAI,CAAC,EAAE;QACnB,WAAW,EACT,4GAA4G;KAC/G,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wEAAwE,EAAE,CAAC,CAAA;IACvH,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAA;IAC7D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gEAUP;AAMK;IAJL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,oDAAsB,CAAC,EAAE;QACrC,WAAW,EAAE,uDAAuD;KACrE,CAAC;IACD,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kEAYP;AAMK;IAJL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EAAE,yFAAyF;KACvG,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAChD,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;wDAS9D;AAIK;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;IAC7F,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9D,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAA;IAC7D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;uDAgBP;AAIK;IAFL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,2BAAU,CAAC,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;IAC5F,IAAA,wBAAS,EAAC,sDAAsD,CAAC;IAE/D,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9D,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAaP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE;QAC3B,WAAW,EACT,sMAAsM;KACzM,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAwBP;AAOK;IAJL,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE;QAC3B,WAAW,EAAE,+FAA+F;KAC7G,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8DAGP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE;QAC3B,WAAW,EACT,gHAAgH;KACnH,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAA;IAClG,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DA6BP;AAOK;IALL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EAAE,mCAAmC;KACjD,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IACX,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;4DAQP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,0NAA0N;KAC7N,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;4DAuCP;AAQK;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE;QACvB,WAAW,EACT,sRAAsR;KACzR,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,6BAAW,CAAC,CAAA;IAC/B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;6DAuDP;AAOK;IALL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,2BAAU,EAAE;QAC1B,WAAW,EAAE,8FAA8F;KAC5G,CAAC;IACD,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAChB,mBAAA,IAAA,kBAAG,EAAC,KAAK,EAAE,GAAG,EAAE,CAAC,6BAAW,CAAC,CAAA;IAC7B,mBAAA,IAAA,kBAAG,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAClC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;4DAuDP;AAKK;IAHL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;IACjG,IAAA,wBAAS,EAAC,yDAAyD,CAAC;IAElE,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAKP;8BAnsBU,mBAAmB;IAD/B,IAAA,uBAAQ,GAAE;GACE,mBAAmB,CAosB/B","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
|
*
|
|
@@ -116,6 +125,22 @@ export interface ToolUsage {
|
|
|
116
125
|
result: any;
|
|
117
126
|
/** read tool / write tool 분류 */
|
|
118
127
|
kind: 'read' | 'write' | 'unknown';
|
|
128
|
+
/**
|
|
129
|
+
* 몇 번째 LLM 턴에서 불렀는가(0부터). 판단 과정을 단계로 읽을 수 있게 한다 —
|
|
130
|
+
* "조회 → 거절 → 정정 → 제안" 이 한 턴 안의 일인지 여러 턴에 걸친 일인지가 진단의 절반이다.
|
|
131
|
+
*/
|
|
132
|
+
iter?: number;
|
|
133
|
+
/**
|
|
134
|
+
* 이 호출이 **어떻게 끝났는가** — 화면이 배지로 보여줄 판정.
|
|
135
|
+
* ok 조회·실행이 정상 완료
|
|
136
|
+
* rejected 도구가 거절(인자 누락·대상 없음 등). 무엇이 빠졌는지는 result 에 있다.
|
|
137
|
+
* queued 보드 편집처럼 클라이언트에서 적용될 예정
|
|
138
|
+
* proposed 조치 제안이 만들어졌다(실행 아님 — 사용자가 버튼을 누른다)
|
|
139
|
+
* folded 같은 효과의 제안이 이미 있어 접혔다. **조용히 버리지 않는다** — 접힌 사실이 보여야
|
|
140
|
+
* "왜 카드가 하나뿐인가" 를 추적할 수 있다.
|
|
141
|
+
* error 알 수 없는 도구·검증 실패
|
|
142
|
+
*/
|
|
143
|
+
outcome?: 'ok' | 'rejected' | 'queued' | 'proposed' | 'folded' | 'error';
|
|
119
144
|
}
|
|
120
145
|
export interface ChatResponse {
|
|
121
146
|
/** 사용자에게 보여줄 텍스트 응답 */
|
|
@@ -131,6 +156,20 @@ export interface ChatResponse {
|
|
|
131
156
|
followUp?: string;
|
|
132
157
|
/** AI 가 응답 만드는 과정에서 호출한 도구들 (시간순). UI fold-able 박스용. */
|
|
133
158
|
toolUsages?: ToolUsage[];
|
|
159
|
+
/**
|
|
160
|
+
* **조치 제안** — 도구가 실행하지 않고 제안만 한 것들. 실행은 사용자가 버튼으로 한다.
|
|
161
|
+
*
|
|
162
|
+
* 규약: 도구 결과에 `proposed: true` 가 있으면 제안이다(도메인 무관 — board-ai 는 내용을 해석하지
|
|
163
|
+
* 않고 그대로 전달한다). 되돌릴 수 없는 조치를 AI 가 직접 실행하지 않게 하는 경계가 이 채널이다.
|
|
164
|
+
*/
|
|
165
|
+
proposals?: any[];
|
|
166
|
+
/**
|
|
167
|
+
* 접지 경고 — 답이 언급했으나 모델이 받은 근거(프롬프트·보드 문맥·이력·도구 결과)에 없는 식별자.
|
|
168
|
+
*
|
|
169
|
+
* 있으면 그 대상은 **만들어 낸 것일 수 있다**. 답을 막거나 고치지 않고 사용자에게 표시한다 —
|
|
170
|
+
* 판정 규칙·사정거리는 ./grounding 참조. 비면(undefined) 접지 정상.
|
|
171
|
+
*/
|
|
172
|
+
groundingWarnings?: string[];
|
|
134
173
|
}
|
|
135
174
|
export interface BoardEditPatch {
|
|
136
175
|
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 * 몇 번째 LLM 턴에서 불렀는가(0부터). 판단 과정을 단계로 읽을 수 있게 한다 —\n * \"조회 → 거절 → 정정 → 제안\" 이 한 턴 안의 일인지 여러 턴에 걸친 일인지가 진단의 절반이다.\n */\n iter?: number\n /**\n * 이 호출이 **어떻게 끝났는가** — 화면이 배지로 보여줄 판정.\n * ok 조회·실행이 정상 완료\n * rejected 도구가 거절(인자 누락·대상 없음 등). 무엇이 빠졌는지는 result 에 있다.\n * queued 보드 편집처럼 클라이언트에서 적용될 예정\n * proposed 조치 제안이 만들어졌다(실행 아님 — 사용자가 버튼을 누른다)\n * folded 같은 효과의 제안이 이미 있어 접혔다. **조용히 버리지 않는다** — 접힌 사실이 보여야\n * \"왜 카드가 하나뿐인가\" 를 추적할 수 있다.\n * error 알 수 없는 도구·검증 실패\n */\n outcome?: 'ok' | 'rejected' | 'queued' | 'proposed' | 'folded' | 'error'\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 * 규약: 도구 결과에 `proposed: true` 가 있으면 제안이다(도메인 무관 — board-ai 는 내용을 해석하지\n * 않고 그대로 전달한다). 되돌릴 수 없는 조치를 AI 가 직접 실행하지 않게 하는 경계가 이 채널이다.\n */\n proposals?: any[]\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"]}
|