@things-factory/ai-assistant 10.1.6 → 10.1.8
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 +165 -100
- package/client/components/chat-defaults.ts +13 -0
- package/client/components/chat-input-builder.test.ts +81 -0
- package/client/components/chat-input-builder.ts +43 -0
- package/client/components/chat-triggers.test.ts +72 -0
- package/client/components/chat-triggers.ts +62 -0
- package/dist-client/components/board-ai-chat.d.ts +8 -0
- package/dist-client/components/board-ai-chat.js +160 -100
- package/dist-client/components/board-ai-chat.js.map +1 -1
- package/dist-client/components/chat-defaults.d.ts +12 -0
- package/dist-client/components/chat-defaults.js +2 -0
- package/dist-client/components/chat-defaults.js.map +1 -1
- package/dist-client/components/chat-input-builder.d.ts +29 -0
- package/dist-client/components/chat-input-builder.js +22 -0
- package/dist-client/components/chat-input-builder.js.map +1 -1
- package/dist-client/components/chat-input-builder.test.d.ts +1 -0
- package/dist-client/components/chat-input-builder.test.js +67 -0
- package/dist-client/components/chat-input-builder.test.js.map +1 -0
- package/dist-client/components/chat-triggers.d.ts +16 -0
- package/dist-client/components/chat-triggers.js +42 -0
- package/dist-client/components/chat-triggers.js.map +1 -0
- package/dist-client/components/chat-triggers.test.d.ts +1 -0
- package/dist-client/components/chat-triggers.test.js +61 -0
- package/dist-client/components/chat-triggers.test.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/assistant-chat-resolver.d.ts +32 -0
- package/dist-server/service/assistant-chat-resolver.js +342 -0
- package/dist-server/service/assistant-chat-resolver.js.map +1 -0
- package/dist-server/service/chat-session-resolver.js +18 -18
- package/dist-server/service/chat-session-resolver.js.map +1 -1
- package/dist-server/service/index.d.ts +3 -1
- package/dist-server/service/index.js +4 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/server/service/assistant-chat-resolver.ts +326 -0
- package/server/service/chat-session-resolver.ts +18 -18
- package/server/service/index.ts +4 -0
- package/test/translations.test.ts +79 -0
- package/translations/en.json +59 -0
- package/translations/ja.json +58 -0
- package/translations/ko.json +59 -0
- package/translations/ms.json +58 -0
- package/translations/zh.json +58 -0
|
@@ -25,7 +25,7 @@ import { i18next } from '@operato/i18n';
|
|
|
25
25
|
import { ScrollbarStyles } from '@operato/styles';
|
|
26
26
|
import gql from 'graphql-tag';
|
|
27
27
|
import { renderMarkdown, injectMentionRefids, injectMentionUserIds, stripMentionRefids } from './markdown.js';
|
|
28
|
-
import { buildChatMutationInput } from './chat-input-builder.js';
|
|
28
|
+
import { buildAssistantChatInput, buildChatMutationInput } from './chat-input-builder.js';
|
|
29
29
|
import { receiveRemoteMessage, reconcileSentLine, remoteMessageToLine } from './chat-echo-dedup.js';
|
|
30
30
|
import './mention-popup.js';
|
|
31
31
|
import { buildMentionCandidates, buildCatalogCandidates, buildSlashCandidates, buildUserCandidates, extractMentionContext, filterMentionCandidates, applyMentionSelection, resolveMentionToken } from './mention-popup.js';
|
|
@@ -41,6 +41,7 @@ import { buildMentionCandidates, buildCatalogCandidates, buildSlashCandidates, b
|
|
|
41
41
|
* 뜨지 않는 것이 맞다.
|
|
42
42
|
*/
|
|
43
43
|
import { chatDefaults } from './chat-defaults.js';
|
|
44
|
+
import { availableTriggers } from './chat-triggers.js';
|
|
44
45
|
const BOARD_AI_CHAT_MUTATION = gql `
|
|
45
46
|
mutation BoardAIChat($input: BoardAIChatInput!) {
|
|
46
47
|
boardAIChat(input: $input) {
|
|
@@ -60,6 +61,29 @@ const BOARD_AI_CHAT_MUTATION = gql `
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
`;
|
|
64
|
+
/*
|
|
65
|
+
* The neutral door. Selected by `chatEndpoint`, and it asks for fewer fields because there is
|
|
66
|
+
* less to ask for: no patch, no scene actions, no patch id — this surface has no document.
|
|
67
|
+
*
|
|
68
|
+
* `offeredTools` has no counterpart on the board side. It is what the server actually put in
|
|
69
|
+
* front of the model this turn, so a surface that granted a category and sees it missing knows
|
|
70
|
+
* the registration never happened, instead of reading a vague answer as a refusal.
|
|
71
|
+
*/
|
|
72
|
+
const ASSISTANT_CHAT_MUTATION = gql `
|
|
73
|
+
mutation AssistantChat($input: AssistantChatInput!) {
|
|
74
|
+
assistantChat(input: $input) {
|
|
75
|
+
reply
|
|
76
|
+
clientId
|
|
77
|
+
sessionId
|
|
78
|
+
userMessageId
|
|
79
|
+
assistantMessageId
|
|
80
|
+
toolUsages
|
|
81
|
+
offeredTools
|
|
82
|
+
proposals
|
|
83
|
+
groundingWarnings
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
63
87
|
const CHAT_MESSAGES_QUERY = gql `
|
|
64
88
|
query ChatMessages($sessionId: String!, $limit: Int, $offset: Int) {
|
|
65
89
|
chatMessages(sessionId: $sessionId, limit: $limit, offset: $offset) {
|
|
@@ -151,6 +175,22 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
151
175
|
this.selectedRefids = [];
|
|
152
176
|
/** 코어 보드 편집 도구 사용 여부. 기본 true. 보드를 고칠 이유가 없는 면에서 false. */
|
|
153
177
|
this.boardTools = true;
|
|
178
|
+
/*
|
|
179
|
+
* Which server operation this surface talks to. A closed set of two, named after the operations
|
|
180
|
+
* themselves so a reader can grep either way.
|
|
181
|
+
*
|
|
182
|
+
* boardAIChat board authoring — lives in @things-factory/board-ai, carries the board model,
|
|
183
|
+
* returns patches and scene actions
|
|
184
|
+
* assistantChat a surface with no document of its own — lives here, runs registered tools only
|
|
185
|
+
*
|
|
186
|
+
* Default stays `boardAIChat`: every existing consumer is a board surface, and a default that
|
|
187
|
+
* changed under them would move their conversation to a door with no board tools behind it.
|
|
188
|
+
*
|
|
189
|
+
* A host that has no board must pass `assistantChat` **and** `systemPrompt`. Until it does, the
|
|
190
|
+
* chat calls a mutation that lives in a package it may not even load — which is how
|
|
191
|
+
* operato-figure got `Cannot query field "boardAIChat" on type "Mutation"` (2026-09-11).
|
|
192
|
+
*/
|
|
193
|
+
this.chatEndpoint = 'boardAIChat';
|
|
154
194
|
/**
|
|
155
195
|
* 라이브 상태를 다루는 대화면인가 — true 면 **첫 턴에 도구 호출을 강제**한다.
|
|
156
196
|
*
|
|
@@ -235,7 +275,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
235
275
|
]);
|
|
236
276
|
}
|
|
237
277
|
else {
|
|
238
|
-
this.showToast(i18next.t('
|
|
278
|
+
this.showToast(i18next.t('ai-assistant.text.mention-not-found', {
|
|
239
279
|
token,
|
|
240
280
|
defaultValue: `#${token} 을(를) 찾을 수 없습니다`
|
|
241
281
|
}));
|
|
@@ -1723,7 +1763,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1723
1763
|
this._seenMessageIds = new Set(msgs.map((m) => m.id).filter(Boolean));
|
|
1724
1764
|
}
|
|
1725
1765
|
catch (e) {
|
|
1726
|
-
this.errorMessage = `${i18next.t('
|
|
1766
|
+
this.errorMessage = `${i18next.t('ai-assistant.text.failed-to-load-history')}: ${e.message ?? e}`;
|
|
1727
1767
|
}
|
|
1728
1768
|
}
|
|
1729
1769
|
render() {
|
|
@@ -1753,9 +1793,9 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1753
1793
|
const showActions = line.role !== 'system' && !line.pending && content.length > 0;
|
|
1754
1794
|
return html `
|
|
1755
1795
|
${showLabel
|
|
1756
|
-
? html `<div class="msg-label">${i18next.t('
|
|
1796
|
+
? html `<div class="msg-label">${i18next.t('ai-assistant.label.ai')}</div>`
|
|
1757
1797
|
: showSenderLabel
|
|
1758
|
-
? html `<div class="msg-label sender">${line.senderName || i18next.t('
|
|
1798
|
+
? html `<div class="msg-label sender">${line.senderName || i18next.t('ai-assistant.label.participant')}</div>`
|
|
1759
1799
|
: nothing}
|
|
1760
1800
|
<div class="msg-wrap ${line.role} ${isOther ? 'other' : ''}">
|
|
1761
1801
|
<div
|
|
@@ -1769,7 +1809,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1769
1809
|
: line.role === 'system'
|
|
1770
1810
|
? html `<span
|
|
1771
1811
|
class="sys-text"
|
|
1772
|
-
title=${i18next.t('
|
|
1812
|
+
title=${i18next.t('ai-assistant.text.toggle-system-note', { defaultValue: '눌러서 펼치기' })}
|
|
1773
1813
|
@click=${() => this.toggleSystemNote(idx)}
|
|
1774
1814
|
>${content}</span
|
|
1775
1815
|
>`
|
|
@@ -1780,14 +1820,14 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1780
1820
|
<md-icon>check_circle</md-icon>
|
|
1781
1821
|
<span class="summary-text">
|
|
1782
1822
|
${this.revertedPatchIds.has(line.patchId)
|
|
1783
|
-
? i18next.t('
|
|
1784
|
-
: i18next.t('
|
|
1823
|
+
? i18next.t('ai-assistant.text.reverted')
|
|
1824
|
+
: i18next.t('ai-assistant.text.applied-changes')}
|
|
1785
1825
|
</span>
|
|
1786
1826
|
<button
|
|
1787
1827
|
class="revert-btn"
|
|
1788
1828
|
?disabled=${this.revertedPatchIds.has(line.patchId)}
|
|
1789
1829
|
@click=${() => line.patchId && this.onRevertClick(line.patchId)}>
|
|
1790
|
-
${i18next.t('
|
|
1830
|
+
${i18next.t('ai-assistant.button.revert')}
|
|
1791
1831
|
</button>
|
|
1792
1832
|
</div>
|
|
1793
1833
|
`
|
|
@@ -1816,7 +1856,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1816
1856
|
${this.lastFailedInput
|
|
1817
1857
|
? html `
|
|
1818
1858
|
<button class="retry-btn" @click=${this.retryLastSend}>
|
|
1819
|
-
${i18next.t('
|
|
1859
|
+
${i18next.t('ai-assistant.button.retry')}
|
|
1820
1860
|
</button>
|
|
1821
1861
|
`
|
|
1822
1862
|
: nothing}
|
|
@@ -1831,11 +1871,11 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1831
1871
|
<button
|
|
1832
1872
|
class="action"
|
|
1833
1873
|
@click=${() => (this.examplesOpen = !this.examplesOpen)}
|
|
1834
|
-
title=${i18next.t('
|
|
1874
|
+
title=${i18next.t('ai-assistant.text.show-examples-tooltip')}>
|
|
1835
1875
|
<md-icon>${this.examplesOpen ? 'close' : 'lightbulb'}</md-icon>
|
|
1836
1876
|
${this.examplesOpen
|
|
1837
|
-
? i18next.t('
|
|
1838
|
-
: i18next.t('
|
|
1877
|
+
? i18next.t('ai-assistant.button.close')
|
|
1878
|
+
: i18next.t('ai-assistant.button.examples')}
|
|
1839
1879
|
</button>
|
|
1840
1880
|
</div>
|
|
1841
1881
|
${this.examplesOpen ? this.renderInlineExamples() : nothing}
|
|
@@ -1854,7 +1894,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1854
1894
|
}}
|
|
1855
1895
|
?disabled=${this.busy}
|
|
1856
1896
|
rows="1"
|
|
1857
|
-
placeholder=${this.placeholder ?? i18next.t('
|
|
1897
|
+
placeholder=${this.placeholder ?? i18next.t('ai-assistant.text.input-placeholder')}></textarea>
|
|
1858
1898
|
${this.mentionOpen
|
|
1859
1899
|
? html `
|
|
1860
1900
|
<ox-mention-popup
|
|
@@ -1877,7 +1917,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
1877
1917
|
<button
|
|
1878
1918
|
?disabled=${this.busy || !this.input.trim()}
|
|
1879
1919
|
@click=${this.send}
|
|
1880
|
-
title=${i18next.t('
|
|
1920
|
+
title=${i18next.t('ai-assistant.text.send-tooltip')}>
|
|
1881
1921
|
${this.busy
|
|
1882
1922
|
? html `<span class="spinner"></span>`
|
|
1883
1923
|
: html `<md-icon>arrow_upward</md-icon>`}
|
|
@@ -2155,26 +2195,34 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2155
2195
|
items: group.itemKeys.map(key => i18next.t(key))
|
|
2156
2196
|
}));
|
|
2157
2197
|
}
|
|
2198
|
+
/** 이 표면이 실제로 쓸 수 있는 트리거 — 판정은 `chat-triggers.ts` 가 하고 여기서는 사실만 읽는다. */
|
|
2199
|
+
get triggerChips() {
|
|
2200
|
+
return availableTriggers({
|
|
2201
|
+
hasMentionableThings: !!(this.boardProvider || this.currentBoard || this.knownTypes?.length),
|
|
2202
|
+
hasSession: !!this.sessionId,
|
|
2203
|
+
slashTemplateCount: chatDefaults.slashTemplates.length
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2158
2206
|
static { this.EXAMPLE_GROUPS = [
|
|
2159
2207
|
{
|
|
2160
|
-
labelKey: '
|
|
2208
|
+
labelKey: 'ai-assistant.label.create',
|
|
2161
2209
|
itemKeys: [
|
|
2162
|
-
'
|
|
2163
|
-
'
|
|
2210
|
+
'ai-assistant.example.create-monitoring-dashboard',
|
|
2211
|
+
'ai-assistant.example.create-welcome-screen'
|
|
2164
2212
|
]
|
|
2165
2213
|
},
|
|
2166
2214
|
{
|
|
2167
|
-
labelKey: '
|
|
2215
|
+
labelKey: 'ai-assistant.label.edit',
|
|
2168
2216
|
itemKeys: [
|
|
2169
|
-
'
|
|
2170
|
-
'
|
|
2217
|
+
'ai-assistant.example.edit-align-distribute',
|
|
2218
|
+
'ai-assistant.example.edit-resize-board'
|
|
2171
2219
|
]
|
|
2172
2220
|
},
|
|
2173
2221
|
{
|
|
2174
|
-
labelKey: '
|
|
2222
|
+
labelKey: 'ai-assistant.label.style',
|
|
2175
2223
|
itemKeys: [
|
|
2176
|
-
'
|
|
2177
|
-
'
|
|
2224
|
+
'ai-assistant.example.style-dark-mode',
|
|
2225
|
+
'ai-assistant.example.style-rounded-shadow'
|
|
2178
2226
|
]
|
|
2179
2227
|
}
|
|
2180
2228
|
]; }
|
|
@@ -2187,7 +2235,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2187
2235
|
<div class="session-tabs" role="tablist">
|
|
2188
2236
|
${this.sessions.map((s, i) => {
|
|
2189
2237
|
const isActive = s.id === this.sessionId;
|
|
2190
|
-
const fallback = i18next.t('
|
|
2238
|
+
const fallback = i18next.t('ai-assistant.text.session-tab-label', {
|
|
2191
2239
|
n: i + 1,
|
|
2192
2240
|
defaultValue: `세션 ${i + 1}`
|
|
2193
2241
|
});
|
|
@@ -2208,12 +2256,12 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2208
2256
|
})}
|
|
2209
2257
|
<button
|
|
2210
2258
|
class="session-tab-new"
|
|
2211
|
-
title=${i18next.t('
|
|
2259
|
+
title=${i18next.t('ai-assistant.button.new-chat', {
|
|
2212
2260
|
defaultValue: '새 대화'
|
|
2213
2261
|
})}
|
|
2214
2262
|
@click=${this._onSessionCreateClick}>
|
|
2215
2263
|
<md-icon>add</md-icon>
|
|
2216
|
-
<span>${i18next.t('
|
|
2264
|
+
<span>${i18next.t('ai-assistant.button.new-chat', { defaultValue: '새 대화' })}</span>
|
|
2217
2265
|
</button>
|
|
2218
2266
|
</div>
|
|
2219
2267
|
`;
|
|
@@ -2254,36 +2302,28 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2254
2302
|
<div class="empty">
|
|
2255
2303
|
<div class="header">
|
|
2256
2304
|
<md-icon class="icon">auto_awesome</md-icon>
|
|
2257
|
-
<div class="title">${this.intro?.title ?? i18next.t('
|
|
2258
|
-
<div class="subtitle">${this.intro?.subtitle ?? i18next.t('
|
|
2305
|
+
<div class="title">${this.intro?.title ?? i18next.t('ai-assistant.text.empty-title')}</div>
|
|
2306
|
+
<div class="subtitle">${this.intro?.subtitle ?? i18next.t('ai-assistant.text.empty-subtitle')}</div>
|
|
2259
2307
|
</div>
|
|
2260
2308
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
class="trigger-chip"
|
|
2280
|
-
title=${i18next.t('board-ai.text.trigger-insert-tooltip')}
|
|
2281
|
-
@click=${() => this.insertTrigger('/')}>
|
|
2282
|
-
<span class="trigger-char">/</span>
|
|
2283
|
-
<span class="trigger-desc">${i18next.t('board-ai.label.trigger-insert')}</span>
|
|
2284
|
-
</button>
|
|
2285
|
-
</div>
|
|
2286
|
-
</div>
|
|
2309
|
+
${this.triggerChips.length > 0
|
|
2310
|
+
? html `
|
|
2311
|
+
<div class="trigger-row">
|
|
2312
|
+
<div class="trigger-row-label">${i18next.t('ai-assistant.text.triggers-heading')}</div>
|
|
2313
|
+
<div class="trigger-chips">
|
|
2314
|
+
${this.triggerChips.map(trigger => html `
|
|
2315
|
+
<button
|
|
2316
|
+
class="trigger-chip"
|
|
2317
|
+
title=${i18next.t(trigger.tooltipKey)}
|
|
2318
|
+
@click=${() => this.insertTrigger(trigger.char)}>
|
|
2319
|
+
<span class="trigger-char">${trigger.char}</span>
|
|
2320
|
+
<span class="trigger-desc">${i18next.t(trigger.labelKey)}</span>
|
|
2321
|
+
</button>
|
|
2322
|
+
`)}
|
|
2323
|
+
</div>
|
|
2324
|
+
</div>
|
|
2325
|
+
`
|
|
2326
|
+
: ''}
|
|
2287
2327
|
|
|
2288
2328
|
${this.exampleGroups.map(group => html `
|
|
2289
2329
|
<div class="group">
|
|
@@ -2297,11 +2337,12 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2297
2337
|
`)}
|
|
2298
2338
|
|
|
2299
2339
|
<div class="footer">
|
|
2300
|
-
<span class="badge">${i18next.t('
|
|
2301
|
-
<span class="badge">${i18next.t('
|
|
2302
|
-
<span class="badge">${i18next.t('
|
|
2303
|
-
|
|
2304
|
-
|
|
2340
|
+
<span class="badge">${i18next.t('ai-assistant.label.korean-supported')}</span>
|
|
2341
|
+
<span class="badge">${i18next.t('ai-assistant.label.multi-command')}</span>
|
|
2342
|
+
<span class="badge">${i18next.t('ai-assistant.label.review-able')}</span>
|
|
2343
|
+
${chatDefaults.footerNoticeKey
|
|
2344
|
+
? html `<br />${i18next.t(chatDefaults.footerNoticeKey)}`
|
|
2345
|
+
: ''}
|
|
2305
2346
|
</div>
|
|
2306
2347
|
</div>
|
|
2307
2348
|
`;
|
|
@@ -2370,15 +2411,15 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2370
2411
|
<button
|
|
2371
2412
|
class="msg-action ${isCopied ? 'confirmed' : ''}"
|
|
2372
2413
|
title=${isCopied
|
|
2373
|
-
? i18next.t('
|
|
2374
|
-
: i18next.t('
|
|
2414
|
+
? i18next.t('ai-assistant.text.copied')
|
|
2415
|
+
: i18next.t('ai-assistant.button.copy')}
|
|
2375
2416
|
@click=${() => this.copyMessage(idx, content)}>
|
|
2376
2417
|
<md-icon>${isCopied ? 'check' : 'content_copy'}</md-icon>
|
|
2377
2418
|
</button>
|
|
2378
2419
|
<button
|
|
2379
2420
|
class="msg-action"
|
|
2380
2421
|
?disabled=${this.busy}
|
|
2381
|
-
title=${i18next.t('
|
|
2422
|
+
title=${i18next.t('ai-assistant.button.regenerate')}
|
|
2382
2423
|
@click=${() => this.regenerateAssistant(idx)}>
|
|
2383
2424
|
<md-icon>refresh</md-icon>
|
|
2384
2425
|
</button>
|
|
@@ -2403,19 +2444,19 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2403
2444
|
<md-icon>unfold_less</md-icon>
|
|
2404
2445
|
<span class="fn-text">
|
|
2405
2446
|
${folded.summarized
|
|
2406
|
-
? i18next.t('
|
|
2447
|
+
? i18next.t('ai-assistant.text.history-folded-summarized', {
|
|
2407
2448
|
count: folded.omitted,
|
|
2408
2449
|
defaultValue: '대화가 길어져 앞부분 {count}개가 요약으로 접혔습니다. 다른 주제라면 새 대화가 낫습니다.'
|
|
2409
2450
|
})
|
|
2410
|
-
: i18next.t('
|
|
2451
|
+
: i18next.t('ai-assistant.text.history-folded', {
|
|
2411
2452
|
count: folded.omitted,
|
|
2412
2453
|
defaultValue: '대화가 길어져 앞부분 {count}개가 생략되었습니다. 다른 주제라면 새 대화가 낫습니다.'
|
|
2413
2454
|
})}
|
|
2414
2455
|
</span>
|
|
2415
2456
|
<button class="fn-new" @click=${this.startNewFromNotice}>
|
|
2416
|
-
${i18next.t('
|
|
2457
|
+
${i18next.t('ai-assistant.button.new-chat')}
|
|
2417
2458
|
</button>
|
|
2418
|
-
<button class="fn-close" title=${i18next.t('
|
|
2459
|
+
<button class="fn-close" title=${i18next.t('ai-assistant.button.close')} @click=${() => (this.foldNoticeDismissed = true)}>
|
|
2419
2460
|
<md-icon>close</md-icon>
|
|
2420
2461
|
</button>
|
|
2421
2462
|
</div>
|
|
@@ -2448,8 +2489,8 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2448
2489
|
class="run"
|
|
2449
2490
|
?disabled=${sent}
|
|
2450
2491
|
@click=${() => this.executeProposal(p, key)}>
|
|
2451
|
-
${sent ? i18next.t('
|
|
2452
|
-
: i18next.t('
|
|
2492
|
+
${sent ? i18next.t('ai-assistant.text.proposal-sent', { defaultValue: '실행 요청됨' })
|
|
2493
|
+
: i18next.t('ai-assistant.button.run-proposal', { defaultValue: '실행' })}
|
|
2453
2494
|
</button>
|
|
2454
2495
|
</div>
|
|
2455
2496
|
`;
|
|
@@ -2509,7 +2550,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2509
2550
|
<div class="grounding-warning" role="note">
|
|
2510
2551
|
<md-icon>report</md-icon>
|
|
2511
2552
|
<span>
|
|
2512
|
-
${i18next.t('
|
|
2553
|
+
${i18next.t('ai-assistant.text.ungrounded-mention', {
|
|
2513
2554
|
defaultValue: '확인되지 않은 대상을 언급했습니다 — 실제 데이터에 없습니다:'
|
|
2514
2555
|
})}
|
|
2515
2556
|
<span class="ids">${ids.join(', ')}</span>
|
|
@@ -2531,7 +2572,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2531
2572
|
const open = !!line.toolUsagesOpen;
|
|
2532
2573
|
const readCount = usages.filter(u => u.kind === 'read').length;
|
|
2533
2574
|
const writeCount = usages.filter(u => u.kind === 'write').length;
|
|
2534
|
-
const toolsLabel = i18next.t('
|
|
2575
|
+
const toolsLabel = i18next.t('ai-assistant.text.tools-used', { defaultValue: '도구 사용' });
|
|
2535
2576
|
/* 눈에 걸려야 하는 것을 머리줄에 올린다 — 거절·접힘이 있으면 펼치기 전에 보인다.
|
|
2536
2577
|
* (이번 사고들이 전부 그것이었다: 인자 빠진 호출이 거절됐고, 중복 제안이 접혔다.) */
|
|
2537
2578
|
const problems = usages.filter(u => u.outcome === 'rejected' || u.outcome === 'error').length;
|
|
@@ -2547,12 +2588,12 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2547
2588
|
<strong>${usages.length}</strong> ${toolsLabel}
|
|
2548
2589
|
<span class="tool-usages-counts">
|
|
2549
2590
|
· read ${readCount} · write ${writeCount}${problems
|
|
2550
|
-
? html ` · <span class="tu-flag bad">${i18next.t('
|
|
2591
|
+
? html ` · <span class="tu-flag bad">${i18next.t('ai-assistant.text.tool-rejected-count', {
|
|
2551
2592
|
count: problems,
|
|
2552
2593
|
defaultValue: '거절 {count}'
|
|
2553
2594
|
})}</span>`
|
|
2554
2595
|
: nothing}${folded
|
|
2555
|
-
? html ` · <span class="tu-flag">${i18next.t('
|
|
2596
|
+
? html ` · <span class="tu-flag">${i18next.t('ai-assistant.text.tool-folded-count', {
|
|
2556
2597
|
count: folded,
|
|
2557
2598
|
defaultValue: '접힘 {count}'
|
|
2558
2599
|
})}</span>`
|
|
@@ -2595,7 +2636,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2595
2636
|
return html `
|
|
2596
2637
|
${newTurn
|
|
2597
2638
|
? html `<li class="tu-turn">
|
|
2598
|
-
${i18next.t('
|
|
2639
|
+
${i18next.t('ai-assistant.text.tool-turn', { n: (u.iter ?? 0) + 1, defaultValue: '{n}번째 판단' })}
|
|
2599
2640
|
</li>`
|
|
2600
2641
|
: nothing}
|
|
2601
2642
|
<li class="tool-usage-item kind-${u.kind} outcome-${outcome}">
|
|
@@ -2620,12 +2661,12 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2620
2661
|
/** 배지 문구 — 코드가 아니라 사람 말로. */
|
|
2621
2662
|
outcomeLabel(outcome) {
|
|
2622
2663
|
const map = {
|
|
2623
|
-
ok: ['
|
|
2624
|
-
rejected: ['
|
|
2625
|
-
queued: ['
|
|
2626
|
-
proposed: ['
|
|
2627
|
-
folded: ['
|
|
2628
|
-
error: ['
|
|
2664
|
+
ok: ['ai-assistant.text.outcome-ok', '조회'],
|
|
2665
|
+
rejected: ['ai-assistant.text.outcome-rejected', '거절'],
|
|
2666
|
+
queued: ['ai-assistant.text.outcome-queued', '적용 예정'],
|
|
2667
|
+
proposed: ['ai-assistant.text.outcome-proposed', '제안'],
|
|
2668
|
+
folded: ['ai-assistant.text.outcome-folded', '중복 접힘'],
|
|
2669
|
+
error: ['ai-assistant.text.outcome-error', '오류']
|
|
2629
2670
|
};
|
|
2630
2671
|
const [key, fallback] = map[outcome] ?? map.ok;
|
|
2631
2672
|
return i18next.t(key, { defaultValue: fallback });
|
|
@@ -2684,7 +2725,7 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2684
2725
|
// 클립보드에는 marker 제거된 깨끗한 텍스트
|
|
2685
2726
|
await navigator.clipboard.writeText(stripMentionRefids(content));
|
|
2686
2727
|
this.copiedIdx = idx;
|
|
2687
|
-
this.showToast(i18next.t('
|
|
2728
|
+
this.showToast(i18next.t('ai-assistant.text.copied'));
|
|
2688
2729
|
setTimeout(() => {
|
|
2689
2730
|
if (this.copiedIdx === idx)
|
|
2690
2731
|
this.copiedIdx = undefined;
|
|
@@ -2791,30 +2832,41 @@ let OxBoardAIChat = class OxBoardAIChat extends LitElement {
|
|
|
2791
2832
|
// boardProvider 가 있으면 send 시점에 그것을 pull, 없으면 정적 currentBoard.
|
|
2792
2833
|
const liveBoard = this.boardProvider ? this.boardProvider() : this.currentBoard;
|
|
2793
2834
|
// mentions 는 위에서 이미 계산해 user line 에 thread — 그대로 mutation 입력으로 재사용.
|
|
2835
|
+
const neutral = this.chatEndpoint === 'assistantChat';
|
|
2794
2836
|
const result = await client.mutate({
|
|
2795
|
-
mutation: BOARD_AI_CHAT_MUTATION,
|
|
2837
|
+
mutation: neutral ? ASSISTANT_CHAT_MUTATION : BOARD_AI_CHAT_MUTATION,
|
|
2796
2838
|
variables: {
|
|
2797
|
-
input:
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2839
|
+
input: neutral
|
|
2840
|
+
? buildAssistantChatInput({
|
|
2841
|
+
sessionId: this.sessionId,
|
|
2842
|
+
history,
|
|
2843
|
+
systemPrompt: this.systemPrompt,
|
|
2844
|
+
truncateAfterMessageId: this._truncateAfterId,
|
|
2845
|
+
hostContext: this.hostContext,
|
|
2846
|
+
toolCategories: this.toolCategories,
|
|
2847
|
+
requireGroundingTools: this.requireGroundingTools
|
|
2848
|
+
})
|
|
2849
|
+
: buildChatMutationInput({
|
|
2850
|
+
sessionId: this.sessionId,
|
|
2851
|
+
history,
|
|
2852
|
+
liveBoard,
|
|
2853
|
+
scopes: this.scopes,
|
|
2854
|
+
truncateAfterMessageId: this._truncateAfterId,
|
|
2855
|
+
hostContext: this.hostContext,
|
|
2856
|
+
toolCategories: this.toolCategories,
|
|
2857
|
+
boardTools: this.boardTools,
|
|
2858
|
+
requireGroundingTools: this.requireGroundingTools,
|
|
2859
|
+
knownTypes: this.knownTypes,
|
|
2860
|
+
categories: this.categories,
|
|
2861
|
+
componentSchemas: this.componentSchemas,
|
|
2862
|
+
selectedRefids: this.selectedRefids ?? [],
|
|
2863
|
+
mentions
|
|
2864
|
+
})
|
|
2813
2865
|
}
|
|
2814
2866
|
});
|
|
2815
|
-
const out = result.data?.boardAIChat;
|
|
2867
|
+
const out = neutral ? result.data?.assistantChat : result.data?.boardAIChat;
|
|
2816
2868
|
if (!out)
|
|
2817
|
-
throw new Error(i18next.t('
|
|
2869
|
+
throw new Error(i18next.t('ai-assistant.text.empty-response'));
|
|
2818
2870
|
/* 대화가 길어졌다는 서버 보고 — 닫지 않았다면 안내를 띄운다. */
|
|
2819
2871
|
this.historyFolded = out.historyFolded ?? undefined;
|
|
2820
2872
|
// 전송 성공 — picks 정리 (다음 메시지부터는 새 picks 만)
|
|
@@ -2915,6 +2967,14 @@ __decorate([
|
|
|
2915
2967
|
property({ type: Boolean, attribute: 'board-tools' }),
|
|
2916
2968
|
__metadata("design:type", Object)
|
|
2917
2969
|
], OxBoardAIChat.prototype, "boardTools", void 0);
|
|
2970
|
+
__decorate([
|
|
2971
|
+
property({ type: String, attribute: 'chat-endpoint' }),
|
|
2972
|
+
__metadata("design:type", String)
|
|
2973
|
+
], OxBoardAIChat.prototype, "chatEndpoint", void 0);
|
|
2974
|
+
__decorate([
|
|
2975
|
+
property({ type: String, attribute: 'system-prompt' }),
|
|
2976
|
+
__metadata("design:type", String)
|
|
2977
|
+
], OxBoardAIChat.prototype, "systemPrompt", void 0);
|
|
2918
2978
|
__decorate([
|
|
2919
2979
|
property({ type: Boolean, attribute: 'require-grounding-tools' }),
|
|
2920
2980
|
__metadata("design:type", Object)
|