@things-factory/ai-assistant 10.1.154 → 10.1.155
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/bootstrap.ts +27 -0
- package/client/components/assistant-chat.ts +229 -178
- package/client/components/assistant-dock.ts +53 -48
- package/client/components/assistant-session-manager.ts +82 -100
- package/client/components/assistant-session-toolbar.ts +11 -3
- package/client/components/chat-input-builder.ts +8 -2
- package/client/settings/agent-settings-model.ts +164 -0
- package/client/settings/ai-agent-setting-let.ts +684 -0
- package/client/settings/ai-usage-setting-let.ts +252 -0
- package/client/settings/usage-model.ts +46 -0
- package/client/setup-assistant-dock.ts +2 -3
- package/client/utils/agent-choice.ts +56 -0
- package/client/utils/assistant-navigation.test.ts +14 -8
- package/client/utils/assistant-navigation.ts +8 -3
- package/client/utils/composer-frame.ts +28 -2
- package/client/utils/menu-room.ts +33 -0
- package/dist-client/bootstrap.d.ts +8 -0
- package/dist-client/bootstrap.js +26 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/assistant-chat.d.ts +32 -4
- package/dist-client/components/assistant-chat.js +234 -175
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/assistant-dock.d.ts +6 -8
- package/dist-client/components/assistant-dock.js +52 -60
- package/dist-client/components/assistant-dock.js.map +1 -1
- package/dist-client/components/assistant-session-manager.d.ts +8 -7
- package/dist-client/components/assistant-session-manager.js +84 -124
- package/dist-client/components/assistant-session-manager.js.map +1 -1
- package/dist-client/components/assistant-session-toolbar.d.ts +1 -0
- package/dist-client/components/assistant-session-toolbar.js +10 -3
- package/dist-client/components/assistant-session-toolbar.js.map +1 -1
- package/dist-client/components/chat-input-builder.d.ts +4 -0
- package/dist-client/components/chat-input-builder.js +4 -2
- package/dist-client/components/chat-input-builder.js.map +1 -1
- package/dist-client/settings/agent-settings-model.d.ts +93 -0
- package/dist-client/settings/agent-settings-model.js +95 -0
- package/dist-client/settings/agent-settings-model.js.map +1 -0
- package/dist-client/settings/ai-agent-setting-let.d.ts +51 -0
- package/dist-client/settings/ai-agent-setting-let.js +672 -0
- package/dist-client/settings/ai-agent-setting-let.js.map +1 -0
- package/dist-client/settings/ai-usage-setting-let.d.ts +26 -0
- package/dist-client/settings/ai-usage-setting-let.js +276 -0
- package/dist-client/settings/ai-usage-setting-let.js.map +1 -0
- package/dist-client/settings/usage-model.d.ts +32 -0
- package/dist-client/settings/usage-model.js +25 -0
- package/dist-client/settings/usage-model.js.map +1 -0
- package/dist-client/setup-assistant-dock.d.ts +0 -1
- package/dist-client/setup-assistant-dock.js +0 -1
- package/dist-client/setup-assistant-dock.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/agent-choice.d.ts +24 -0
- package/dist-client/utils/agent-choice.js +38 -0
- package/dist-client/utils/agent-choice.js.map +1 -0
- package/dist-client/utils/assistant-navigation.d.ts +8 -3
- package/dist-client/utils/assistant-navigation.js +7 -2
- package/dist-client/utils/assistant-navigation.js.map +1 -1
- package/dist-client/utils/assistant-navigation.test.js +14 -8
- package/dist-client/utils/assistant-navigation.test.js.map +1 -1
- package/dist-client/utils/composer-frame.d.ts +23 -2
- package/dist-client/utils/composer-frame.js +20 -2
- package/dist-client/utils/composer-frame.js.map +1 -1
- package/dist-client/utils/menu-room.d.ts +13 -0
- package/dist-client/utils/menu-room.js +26 -0
- package/dist-client/utils/menu-room.js.map +1 -0
- package/dist-server/service/ai-agent-resolver.d.ts +82 -0
- package/dist-server/service/ai-agent-resolver.js +417 -0
- package/dist-server/service/ai-agent-resolver.js.map +1 -0
- package/dist-server/service/assistant-capability-resolver.d.ts +1 -1
- package/dist-server/service/assistant-capability-resolver.js +7 -7
- package/dist-server/service/assistant-capability-resolver.js.map +1 -1
- package/dist-server/service/assistant-chat-resolver.d.ts +2 -0
- package/dist-server/service/assistant-chat-resolver.js +32 -15
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/assistant-mode/resolve.d.ts +0 -1
- package/dist-server/service/assistant-mode/resolve.js +0 -1
- package/dist-server/service/assistant-mode/resolve.js.map +1 -1
- package/dist-server/service/assistant-mode/types.d.ts +1 -2
- package/dist-server/service/assistant-mode/types.js.map +1 -1
- package/dist-server/service/chat-session/chat-session.d.ts +2 -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/index.d.ts +2 -1
- package/dist-server/service/index.js +3 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/testing/mode-conformance.js +0 -1
- package/dist-server/service/testing/mode-conformance.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -7
- package/server/service/ai-agent-resolver.ts +330 -0
- package/server/service/assistant-capability-resolver.ts +7 -7
- package/server/service/assistant-chat-resolver.ts +32 -24
- package/server/service/assistant-mode/resolve.test.ts +0 -2
- package/server/service/assistant-mode/resolve.ts +0 -2
- package/server/service/assistant-mode/types.ts +1 -2
- package/server/service/chat-session/chat-session.ts +5 -0
- package/server/service/index.ts +3 -0
- package/server/service/testing/mode-conformance.ts +0 -1
- package/server/tool-registry-boot-report.test.ts +2 -0
- package/test/ai-agent-settings.test.ts +123 -0
- package/test/assistant-dock.test.ts +3 -2
- package/test/assistant-session-manager.test.ts +10 -4
- package/test/assistant-session-toolbar.test.ts +8 -0
- package/test/composer-frame.test.ts +40 -2
- package/test/menu-room.test.ts +33 -0
- package/test/open-doors.test.ts +3 -1
- package/test/session-discovery.test.ts +7 -6
- package/things-factory.config.js +2 -0
- package/translations/en.json +78 -12
- package/translations/es.json +78 -12
- package/translations/ja.json +78 -12
- package/translations/ko.json +78 -12
- package/translations/ms.json +78 -12
- package/translations/zh.json +78 -12
- package/client/components/chat-triggers.test.ts +0 -72
- package/client/components/chat-triggers.ts +0 -62
- package/dist-client/components/chat-triggers.d.ts +0 -16
- package/dist-client/components/chat-triggers.js +0 -42
- package/dist-client/components/chat-triggers.js.map +0 -1
- package/dist-client/components/chat-triggers.test.d.ts +0 -1
- package/dist-client/components/chat-triggers.test.js +0 -61
- package/dist-client/components/chat-triggers.test.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { html } from 'lit'
|
|
2
|
+
import { store } from '@operato/shell'
|
|
3
|
+
import { auth, hasPrivilege } from '@things-factory/auth-base/dist-client/index.js'
|
|
4
|
+
import { ADD_SETTING } from '@things-factory/setting-base/dist-client/index.js'
|
|
5
|
+
|
|
6
|
+
import './settings/ai-agent-setting-let.js'
|
|
7
|
+
import './settings/ai-usage-setting-let.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Every application that carries the AI assistant gets its domain's AI agents and AI usage in Settings ›
|
|
11
|
+
* {domain} settings (design/ai/agent-settings.md §7). They are the domain's, not the person's, so they go on the
|
|
12
|
+
* tenant tab - and only for someone who may change the domain's settings, as the doors behind them require.
|
|
13
|
+
*/
|
|
14
|
+
export default function bootstrap() {
|
|
15
|
+
auth.on('profile', async () => {
|
|
16
|
+
if (!(await hasPrivilege({ privilege: 'mutation', category: 'setting', domainOwnerGranted: true }))) return
|
|
17
|
+
|
|
18
|
+
store.dispatch({
|
|
19
|
+
type: ADD_SETTING,
|
|
20
|
+
setting: { seq: 60, scope: 'tenant', template: html`<ai-agent-setting-let></ai-agent-setting-let>` }
|
|
21
|
+
})
|
|
22
|
+
store.dispatch({
|
|
23
|
+
type: ADD_SETTING,
|
|
24
|
+
setting: { seq: 61, scope: 'tenant', template: html`<ai-usage-setting-let></ai-usage-setting-let>` }
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -36,10 +36,12 @@ import {
|
|
|
36
36
|
} from './markdown.js'
|
|
37
37
|
import { acceptOf, chatAttachmentUrl, refuseAttachments, textCharsOf } from '../utils/attachment-policy.js'
|
|
38
38
|
import { pastedTableFile } from '../utils/pasted-table.js'
|
|
39
|
-
import { appendWords, composerHeight, composerKeyAction, sendShortcut, tableAsWords } from '../utils/composer-frame.js'
|
|
39
|
+
import { appendWords, composerHeight, composerKeyAction, composerSuggestion, sendShortcut, tableAsWords } from '../utils/composer-frame.js'
|
|
40
40
|
import { readSpreadsheet, spreadsheetKindOf, type SpreadsheetRefusal } from '../utils/spreadsheet-text.js'
|
|
41
41
|
import { entryReviewAddress, offersWorkspaceReview } from '../utils/entry-workspace.js'
|
|
42
42
|
import { buildAssistantChatInput, buildChatMutationInput } from './chat-input-builder.js'
|
|
43
|
+
import { agentDetail, offersAgentPicker, pickAgentId, rememberAgent, rememberedAgent, type AgentChoice } from '../utils/agent-choice.js'
|
|
44
|
+
import { store } from '@operato/shell'
|
|
43
45
|
import { receiveRemoteMessage, reconcileSentLine, remoteMessageToLine } from './chat-echo-dedup.js'
|
|
44
46
|
import './mention-popup.js'
|
|
45
47
|
import {
|
|
@@ -68,7 +70,6 @@ import {
|
|
|
68
70
|
import { chatDefaults } from './chat-defaults.js'
|
|
69
71
|
import { isDifferentConversation, resolveConversation, type AssistantMode, type AssistantModeView, pickInitialMode } from './assistant-mode.js'
|
|
70
72
|
import { parseProposalOutcomeLine, parseProposalRequestLine, proposalCardState, type ParsedProposalOutcome } from './proposal-outcome-line.js'
|
|
71
|
-
import { availableTriggers, type TriggerChip } from './chat-triggers.js'
|
|
72
73
|
import './entry-review.js'
|
|
73
74
|
import { entryGroupOf, entryReadyCount, entryTitle, isChangeProposal, isEntryProposal, latestEntryKeys } from '../utils/entry-proposal.js'
|
|
74
75
|
import { isWorkspaceProposal, versionOfCard, workspaceAddress } from '../utils/workspace.js'
|
|
@@ -235,6 +236,18 @@ const ASSISTANT_MODES_QUERY = gql`
|
|
|
235
236
|
}
|
|
236
237
|
`
|
|
237
238
|
|
|
239
|
+
const AI_AGENT_CHOICES_QUERY = gql`
|
|
240
|
+
query AiAgentChoices {
|
|
241
|
+
aiAgentChoices {
|
|
242
|
+
id
|
|
243
|
+
name
|
|
244
|
+
provider
|
|
245
|
+
model
|
|
246
|
+
isDefault
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
`
|
|
250
|
+
|
|
238
251
|
const ASSISTANT_MODE_QUERY = gql`
|
|
239
252
|
query AssistantMode($kind: String!) {
|
|
240
253
|
assistantMode(kind: $kind) {
|
|
@@ -916,6 +929,23 @@ export class OxAssistantChat extends LitElement {
|
|
|
916
929
|
return { busy: this.busy, draft: !!this.input.trim() || this.attachments.length > 0 }
|
|
917
930
|
}
|
|
918
931
|
|
|
932
|
+
/**
|
|
933
|
+
* What is being written, handed to the host when the page moves to another subject - it belongs to the conversation
|
|
934
|
+
* it was written in, and comes back with `restoreDraft` when the person returns. The box is left empty.
|
|
935
|
+
*/
|
|
936
|
+
takeDraft(): { text: string; attachments: ChatAttachment[] } | undefined {
|
|
937
|
+
if (!this.input.trim() && !this.attachments.length) return undefined
|
|
938
|
+
const draft = { text: this.input, attachments: this.attachments }
|
|
939
|
+
this.input = ''
|
|
940
|
+
this.attachments = []
|
|
941
|
+
return draft
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
restoreDraft(draft: { text: string; attachments: ChatAttachment[] }) {
|
|
945
|
+
this.input = draft.text
|
|
946
|
+
this.attachments = draft.attachments
|
|
947
|
+
}
|
|
948
|
+
|
|
919
949
|
@state()
|
|
920
950
|
private errorMessage?: string
|
|
921
951
|
|
|
@@ -1028,6 +1058,40 @@ export class OxAssistantChat extends LitElement {
|
|
|
1028
1058
|
|
|
1029
1059
|
private modeChoicesToken = 0
|
|
1030
1060
|
|
|
1061
|
+
/** The AI agents this domain lets a person pick (design/ai/agent-settings.md §5). One or none draws no picker. */
|
|
1062
|
+
@state() private agentChoices: AgentChoice[] = []
|
|
1063
|
+
@state() private agentId?: string
|
|
1064
|
+
|
|
1065
|
+
private agentChoicesToken = 0
|
|
1066
|
+
|
|
1067
|
+
private get contextPath(): string | undefined {
|
|
1068
|
+
return (store.getState() as any)?.app?.contextPath
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Asks which agents this domain offers. The list changes when someone edits the domain's agents, so it is read
|
|
1073
|
+
* again whenever the conversation's subject is - never cached for the life of the page.
|
|
1074
|
+
*/
|
|
1075
|
+
private async loadAgentChoices() {
|
|
1076
|
+
const token = ++this.agentChoicesToken
|
|
1077
|
+
try {
|
|
1078
|
+
const result = await client.query({ query: AI_AGENT_CHOICES_QUERY, fetchPolicy: 'network-only' })
|
|
1079
|
+
if (token !== this.agentChoicesToken) return
|
|
1080
|
+
this.agentChoices = result.data?.aiAgentChoices ?? []
|
|
1081
|
+
} catch {
|
|
1082
|
+
/* Not knowing the list leaves the domain default to answer - the picker just does not appear. */
|
|
1083
|
+
if (token !== this.agentChoicesToken) return
|
|
1084
|
+
this.agentChoices = []
|
|
1085
|
+
}
|
|
1086
|
+
this.agentId = pickAgentId(this.agentChoices, rememberedAgent(this.contextPath))
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
private onAgentPick(agentId: string) {
|
|
1090
|
+
if (!this.agentChoices.some(one => one.id === agentId)) return
|
|
1091
|
+
this.agentId = agentId
|
|
1092
|
+
rememberAgent(this.contextPath, agentId)
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1031
1095
|
/**
|
|
1032
1096
|
* 이 화면에 설 수 있는 대화를 서버에 묻는다.
|
|
1033
1097
|
*
|
|
@@ -1211,6 +1275,12 @@ export class OxAssistantChat extends LitElement {
|
|
|
1211
1275
|
padding: 2px 10px;
|
|
1212
1276
|
white-space: nowrap;
|
|
1213
1277
|
}
|
|
1278
|
+
/* The agent sits at the end of the row above the box. */
|
|
1279
|
+
.agent-select {
|
|
1280
|
+
margin-left: auto;
|
|
1281
|
+
max-width: 50%;
|
|
1282
|
+
text-overflow: ellipsis;
|
|
1283
|
+
}
|
|
1214
1284
|
.mode-switch:hover:not([disabled]) {
|
|
1215
1285
|
background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent);
|
|
1216
1286
|
color: var(--primary-color, #007f87);
|
|
@@ -1447,77 +1517,6 @@ export class OxAssistantChat extends LitElement {
|
|
|
1447
1517
|
line-height: 1.6;
|
|
1448
1518
|
text-align: center;
|
|
1449
1519
|
}
|
|
1450
|
-
.empty .footer .badge {
|
|
1451
|
-
display: inline-block;
|
|
1452
|
-
margin: 0 4px;
|
|
1453
|
-
padding: 1px 7px;
|
|
1454
|
-
background: var(--md-sys-color-surface-container, #f8fafc);
|
|
1455
|
-
border-radius: 8px;
|
|
1456
|
-
font-size: 9.5px;
|
|
1457
|
-
color: var(--md-sys-color-on-surface-variant, #64748b);
|
|
1458
|
-
}
|
|
1459
|
-
/* ── Trigger chip row (empty state) ──────────────── */
|
|
1460
|
-
.empty .trigger-row {
|
|
1461
|
-
margin: 12px 0 4px;
|
|
1462
|
-
padding: 10px 12px;
|
|
1463
|
-
background: var(--md-sys-color-surface-container-low, #f8fafc);
|
|
1464
|
-
border: 1px solid var(--md-sys-color-outline-variant, #eef2f6);
|
|
1465
|
-
border-radius: 10px;
|
|
1466
|
-
}
|
|
1467
|
-
.empty .trigger-row-label {
|
|
1468
|
-
font-size: 10px;
|
|
1469
|
-
color: var(--md-sys-color-on-surface-variant, #64748b);
|
|
1470
|
-
margin-bottom: 6px;
|
|
1471
|
-
text-align: center;
|
|
1472
|
-
letter-spacing: 0.02em;
|
|
1473
|
-
}
|
|
1474
|
-
.empty .trigger-chips {
|
|
1475
|
-
display: flex;
|
|
1476
|
-
gap: 6px;
|
|
1477
|
-
justify-content: center;
|
|
1478
|
-
flex-wrap: wrap;
|
|
1479
|
-
}
|
|
1480
|
-
.empty .trigger-chip {
|
|
1481
|
-
display: inline-flex;
|
|
1482
|
-
align-items: center;
|
|
1483
|
-
gap: 5px;
|
|
1484
|
-
padding: 5px 10px;
|
|
1485
|
-
background: var(--md-sys-color-surface, #ffffff);
|
|
1486
|
-
border: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
1487
|
-
border-radius: 18px;
|
|
1488
|
-
cursor: pointer;
|
|
1489
|
-
font-size: 11.5px;
|
|
1490
|
-
color: var(--md-sys-color-on-surface, #0f172a);
|
|
1491
|
-
transition:
|
|
1492
|
-
border-color 0.15s,
|
|
1493
|
-
background 0.15s,
|
|
1494
|
-
color 0.15s;
|
|
1495
|
-
}
|
|
1496
|
-
.empty .trigger-chip:hover {
|
|
1497
|
-
border-color: var(--md-sys-color-primary, #2563eb);
|
|
1498
|
-
background: var(--md-sys-color-primary-container, #dbeafe);
|
|
1499
|
-
color: var(--md-sys-color-on-primary-container, #1e3a8a);
|
|
1500
|
-
}
|
|
1501
|
-
.empty .trigger-chip .trigger-char {
|
|
1502
|
-
font-family:
|
|
1503
|
-
'SF Mono',
|
|
1504
|
-
Menlo,
|
|
1505
|
-
Monaco,
|
|
1506
|
-
Consolas,
|
|
1507
|
-
monospace;
|
|
1508
|
-
font-size: 13px;
|
|
1509
|
-
font-weight: 600;
|
|
1510
|
-
color: var(--md-sys-color-primary, #2563eb);
|
|
1511
|
-
min-width: 12px;
|
|
1512
|
-
text-align: center;
|
|
1513
|
-
}
|
|
1514
|
-
.empty .trigger-chip:hover .trigger-char {
|
|
1515
|
-
color: var(--md-sys-color-on-primary-container, #1e3a8a);
|
|
1516
|
-
}
|
|
1517
|
-
.empty .trigger-chip .trigger-desc {
|
|
1518
|
-
font-weight: 500;
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
1520
|
/* ── Message label (sender hint) ──────────────────── */
|
|
1522
1521
|
.msg-label {
|
|
1523
1522
|
font-size: 9.5px;
|
|
@@ -2435,9 +2434,6 @@ export class OxAssistantChat extends LitElement {
|
|
|
2435
2434
|
.composer.wide textarea {
|
|
2436
2435
|
min-height: 0;
|
|
2437
2436
|
}
|
|
2438
|
-
.composer-size {
|
|
2439
|
-
margin-left: auto;
|
|
2440
|
-
}
|
|
2441
2437
|
/* Named above the send button, out of the row - it takes no width from the box. */
|
|
2442
2438
|
.composer.wide .input-row {
|
|
2443
2439
|
position: relative;
|
|
@@ -2445,7 +2441,7 @@ export class OxAssistantChat extends LitElement {
|
|
|
2445
2441
|
.send-shortcut {
|
|
2446
2442
|
position: absolute;
|
|
2447
2443
|
right: 14px;
|
|
2448
|
-
bottom:
|
|
2444
|
+
bottom: 68px;
|
|
2449
2445
|
font-size: 10px;
|
|
2450
2446
|
color: var(--md-sys-color-outline, #64748b);
|
|
2451
2447
|
white-space: nowrap;
|
|
@@ -2724,6 +2720,34 @@ export class OxAssistantChat extends LitElement {
|
|
|
2724
2720
|
opacity: 0.7;
|
|
2725
2721
|
}
|
|
2726
2722
|
|
|
2723
|
+
/* Low-key words in the row above the box - examples, write wide. Text, no frame (page-standard.md §8 quiet). */
|
|
2724
|
+
.quiet {
|
|
2725
|
+
all: unset;
|
|
2726
|
+
cursor: pointer;
|
|
2727
|
+
font-size: 11px;
|
|
2728
|
+
line-height: 20px;
|
|
2729
|
+
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2730
|
+
}
|
|
2731
|
+
.quiet:hover,
|
|
2732
|
+
.quiet[aria-expanded='true'],
|
|
2733
|
+
.quiet[aria-pressed='true'] {
|
|
2734
|
+
color: var(--md-sys-color-primary, #0f172a);
|
|
2735
|
+
text-decoration: underline;
|
|
2736
|
+
text-underline-offset: 2px;
|
|
2737
|
+
}
|
|
2738
|
+
/* The words in the row read as separate controls only with a mark between them. */
|
|
2739
|
+
.quiet-sep {
|
|
2740
|
+
font-size: 11px;
|
|
2741
|
+
line-height: 20px;
|
|
2742
|
+
color: var(--md-sys-color-outline, #94a3b8);
|
|
2743
|
+
user-select: none;
|
|
2744
|
+
}
|
|
2745
|
+
.quiet:focus-visible {
|
|
2746
|
+
outline: 2px solid var(--md-sys-color-primary, #0f172a);
|
|
2747
|
+
outline-offset: 2px;
|
|
2748
|
+
border-radius: 2px;
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2727
2751
|
/* ── Inline examples (action row 의 "예시" 토글 시) ──── */
|
|
2728
2752
|
.inline-examples {
|
|
2729
2753
|
padding: 4px 16px 0;
|
|
@@ -2771,11 +2795,32 @@ export class OxAssistantChat extends LitElement {
|
|
|
2771
2795
|
.reply-preview strong { display: block; font-size: 12px; }
|
|
2772
2796
|
.reply-preview span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
|
2773
2797
|
.reply-preview button { border: 0; background: transparent; color: inherit; cursor: pointer; }
|
|
2798
|
+
/* Said only when a message goes to people - the AI is who a message goes to otherwise, and that needs no label. */
|
|
2774
2799
|
.send-destination {
|
|
2775
|
-
|
|
2800
|
+
display: flex;
|
|
2801
|
+
align-items: center;
|
|
2802
|
+
gap: 6px;
|
|
2803
|
+
margin: 2px 14px 0;
|
|
2804
|
+
font-size: 11.5px;
|
|
2776
2805
|
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2777
|
-
|
|
2778
|
-
|
|
2806
|
+
}
|
|
2807
|
+
.send-destination md-icon {
|
|
2808
|
+
--md-icon-size: 15px;
|
|
2809
|
+
}
|
|
2810
|
+
/* A notice that must not move the conversation: it floats over the bottom of the messages. */
|
|
2811
|
+
.toast {
|
|
2812
|
+
position: absolute;
|
|
2813
|
+
left: 50%;
|
|
2814
|
+
bottom: 120px;
|
|
2815
|
+
transform: translateX(-50%);
|
|
2816
|
+
z-index: 5;
|
|
2817
|
+
padding: 4px 12px;
|
|
2818
|
+
border-radius: 999px;
|
|
2819
|
+
background: var(--md-sys-color-inverse-surface, #1e293b);
|
|
2820
|
+
color: var(--md-sys-color-inverse-on-surface, #f8fafc);
|
|
2821
|
+
font-size: 11.5px;
|
|
2822
|
+
white-space: nowrap;
|
|
2823
|
+
pointer-events: none;
|
|
2779
2824
|
}
|
|
2780
2825
|
.input-row {
|
|
2781
2826
|
display: flex;
|
|
@@ -2852,42 +2897,57 @@ export class OxAssistantChat extends LitElement {
|
|
|
2852
2897
|
--md-icon-size: 15px;
|
|
2853
2898
|
}
|
|
2854
2899
|
|
|
2855
|
-
/*
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2900
|
+
/*
|
|
2901
|
+
* Send and attach, two flat buttons stacked beside the box - together they are the box's resting height (54), so
|
|
2902
|
+
* the row's top and bottom lines meet. Without attachments the send button stands alone at the bottom.
|
|
2903
|
+
*/
|
|
2904
|
+
.send-stack {
|
|
2905
|
+
display: flex;
|
|
2906
|
+
flex-direction: column;
|
|
2907
|
+
justify-content: flex-end;
|
|
2908
|
+
gap: 4px;
|
|
2909
|
+
flex-shrink: 0;
|
|
2910
|
+
align-self: flex-end;
|
|
2911
|
+
}
|
|
2912
|
+
.send-stack > button {
|
|
2913
|
+
width: 40px;
|
|
2914
|
+
height: 25px;
|
|
2859
2915
|
padding: 0;
|
|
2860
|
-
|
|
2861
|
-
color: var(--md-sys-color-on-primary, #ffffff);
|
|
2862
|
-
border: 0;
|
|
2863
|
-
border-radius: 12px;
|
|
2916
|
+
border-radius: 6px;
|
|
2864
2917
|
cursor: pointer;
|
|
2865
2918
|
display: flex;
|
|
2866
2919
|
align-items: center;
|
|
2867
2920
|
justify-content: center;
|
|
2868
2921
|
transition:
|
|
2869
2922
|
background 0.15s,
|
|
2870
|
-
transform 0.1s,
|
|
2871
2923
|
opacity 0.15s;
|
|
2872
|
-
flex-shrink: 0;
|
|
2873
|
-
align-self: flex-end;
|
|
2874
|
-
margin-bottom: 0;
|
|
2875
2924
|
}
|
|
2876
|
-
.
|
|
2925
|
+
.send-stack > .send {
|
|
2926
|
+
background: var(--md-sys-color-primary, #1e293b);
|
|
2927
|
+
color: var(--md-sys-color-on-primary, #ffffff);
|
|
2928
|
+
border: 0;
|
|
2929
|
+
}
|
|
2930
|
+
.send-stack > .attach {
|
|
2931
|
+
background: transparent;
|
|
2932
|
+
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2933
|
+
border: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2934
|
+
}
|
|
2935
|
+
.send-stack > .send:disabled {
|
|
2877
2936
|
background: var(--md-sys-color-surface-container-highest, #e2e8f0);
|
|
2878
2937
|
color: var(--md-sys-color-outline, #94a3b8);
|
|
2879
2938
|
cursor: not-allowed;
|
|
2880
2939
|
}
|
|
2881
|
-
.
|
|
2940
|
+
.send-stack > .send:hover:not(:disabled) {
|
|
2882
2941
|
opacity: 0.88;
|
|
2883
2942
|
}
|
|
2884
|
-
.
|
|
2885
|
-
|
|
2943
|
+
.send-stack > .attach:hover {
|
|
2944
|
+
background: var(--md-sys-color-surface-container, #f8fafc);
|
|
2945
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
2886
2946
|
}
|
|
2887
|
-
.
|
|
2888
|
-
--md-icon-size:
|
|
2947
|
+
.send-stack md-icon {
|
|
2948
|
+
--md-icon-size: 16px;
|
|
2889
2949
|
}
|
|
2890
|
-
.
|
|
2950
|
+
.send-stack .spinner {
|
|
2891
2951
|
width: 12px;
|
|
2892
2952
|
height: 12px;
|
|
2893
2953
|
border: 1.8px solid currentColor;
|
|
@@ -2911,6 +2971,8 @@ export class OxAssistantChat extends LitElement {
|
|
|
2911
2971
|
/* 모드가 이미 붙어 있으면 무엇을 주는지 먼저 묻는다 — 바뀔 때는 updated 가 다시 묻는다. */
|
|
2912
2972
|
if (this.mode?.kind) this.loadModeView()
|
|
2913
2973
|
if (this.subject) this.onSubjectChanged()
|
|
2974
|
+
/* The domain's agents are read on every mount - a dock put back after closing sees an agent added meanwhile. */
|
|
2975
|
+
else void this.loadAgentChoices()
|
|
2914
2976
|
/*
|
|
2915
2977
|
* Put back after the dock was closed: what is being written stays, the conversation is read again and its
|
|
2916
2978
|
* broadcast and presence start again - they were stopped when it was taken out.
|
|
@@ -2929,6 +2991,7 @@ export class OxAssistantChat extends LitElement {
|
|
|
2929
2991
|
* 대화로 가면 그때 새로 난다(ADR-0072 A17 ①).
|
|
2930
2992
|
*/
|
|
2931
2993
|
private async onSubjectChanged() {
|
|
2994
|
+
void this.loadAgentChoices()
|
|
2932
2995
|
await this.loadModeChoices()
|
|
2933
2996
|
if (this.modeChoices.length < 2 || !this.subject || !this.mode) return
|
|
2934
2997
|
|
|
@@ -3326,40 +3389,31 @@ export class OxAssistantChat extends LitElement {
|
|
|
3326
3389
|
}
|
|
3327
3390
|
}}
|
|
3328
3391
|
/>
|
|
3329
|
-
${this.acceptsAttachments
|
|
3330
|
-
? html`<button
|
|
3331
|
-
type="button"
|
|
3332
|
-
class="action"
|
|
3333
|
-
@click=${() => this.renderRoot.querySelector<HTMLInputElement>('#chat-file-input')?.click()}
|
|
3334
|
-
title=${i18next.t('ai-assistant.tooltip.attach-file', { defaultValue: 'attach an image (drag & drop or paste supported)' })}>
|
|
3335
|
-
<md-icon>attach_file</md-icon>
|
|
3336
|
-
${i18next.t('ai-assistant.button.attach', { defaultValue: 'Attach' })}
|
|
3337
|
-
</button>`
|
|
3338
|
-
: nothing}
|
|
3339
3392
|
${hasMessages
|
|
3340
3393
|
? html`
|
|
3341
3394
|
<button
|
|
3342
3395
|
type="button"
|
|
3343
|
-
class="
|
|
3396
|
+
class="quiet"
|
|
3397
|
+
aria-expanded=${this.examplesOpen ? 'true' : 'false'}
|
|
3344
3398
|
@click=${() => (this.examplesOpen = !this.examplesOpen)}
|
|
3345
3399
|
title=${i18next.t('ai-assistant.text.show-examples-tooltip')}>
|
|
3346
|
-
<md-icon>${this.examplesOpen ? 'close' : 'lightbulb'}</md-icon>
|
|
3347
3400
|
${this.examplesOpen
|
|
3348
3401
|
? i18next.t('ai-assistant.button.close')
|
|
3349
3402
|
: i18next.t('ai-assistant.button.examples')}
|
|
3350
3403
|
</button>
|
|
3351
3404
|
`
|
|
3352
3405
|
: nothing}
|
|
3406
|
+
${hasMessages ? html`<span class="quiet-sep" aria-hidden="true">·</span>` : nothing}
|
|
3353
3407
|
<button
|
|
3354
3408
|
type="button"
|
|
3355
|
-
class="
|
|
3409
|
+
class="quiet composer-size"
|
|
3356
3410
|
aria-pressed=${this.composerWide ? 'true' : 'false'}
|
|
3357
3411
|
@click=${() => this.setComposerWide(!this.composerWide)}>
|
|
3358
|
-
<md-icon>${this.composerWide ? 'close_fullscreen' : 'open_in_full'}</md-icon>
|
|
3359
3412
|
${this.composerWide
|
|
3360
3413
|
? i18next.t('ai-assistant.button.write-narrow', { defaultValue: 'Narrow' })
|
|
3361
3414
|
: i18next.t('ai-assistant.button.write-wide', { defaultValue: 'Write wide' })}
|
|
3362
3415
|
</button>
|
|
3416
|
+
${this.renderAgentPicker()}
|
|
3363
3417
|
</div>
|
|
3364
3418
|
${this.renderTablePreview()}
|
|
3365
3419
|
${this.examplesOpen ? this.renderInlineExamples() : nothing}
|
|
@@ -3408,6 +3462,13 @@ export class OxAssistantChat extends LitElement {
|
|
|
3408
3462
|
@click=${() => this.cancelReply()}><md-icon>close</md-icon></button>
|
|
3409
3463
|
</div>` : nothing}
|
|
3410
3464
|
${this.renderInviteNotice()}
|
|
3465
|
+
${this.addressesPeople
|
|
3466
|
+
? html`<div class="send-destination" aria-live="polite">
|
|
3467
|
+
<md-icon>group</md-icon>${i18next.t('ai-assistant.text.send-to-people-notice', {
|
|
3468
|
+
defaultValue: 'This goes to the people in this conversation. The AI does not answer it.'
|
|
3469
|
+
})}
|
|
3470
|
+
</div>`
|
|
3471
|
+
: nothing}
|
|
3411
3472
|
<div class="input-row">
|
|
3412
3473
|
<div class="textarea-wrap">
|
|
3413
3474
|
<textarea
|
|
@@ -3421,9 +3482,7 @@ export class OxAssistantChat extends LitElement {
|
|
|
3421
3482
|
}}
|
|
3422
3483
|
?disabled=${this.busy}
|
|
3423
3484
|
rows="1"
|
|
3424
|
-
placeholder=${this.conversation.placeholder ??
|
|
3425
|
-
this.placeholder ??
|
|
3426
|
-
i18next.t('ai-assistant.text.input-placeholder')}></textarea>
|
|
3485
|
+
placeholder=${this.suggestion ?? this.conversation.placeholder ?? this.placeholder ?? i18next.t('ai-assistant.text.input-placeholder')}></textarea>
|
|
3427
3486
|
${this.mentionOpen
|
|
3428
3487
|
? html`
|
|
3429
3488
|
<ox-mention-popup
|
|
@@ -3444,12 +3503,12 @@ export class OxAssistantChat extends LitElement {
|
|
|
3444
3503
|
`
|
|
3445
3504
|
: nothing}
|
|
3446
3505
|
</div>
|
|
3447
|
-
<span class="send-destination" aria-live="polite">${i18next.t(this.addressesPeople
|
|
3448
|
-
? 'ai-assistant.text.send-to-people' : 'ai-assistant.text.send-to-ai')}</span>
|
|
3449
3506
|
${this.composerWide
|
|
3450
3507
|
? html`<span class="send-shortcut">${i18next.t('ai-assistant.text.send-shortcut', { key: sendShortcut(navigator.platform), defaultValue: '{key} to send' })}</span>`
|
|
3451
3508
|
: nothing}
|
|
3509
|
+
<div class="send-stack">
|
|
3452
3510
|
<button
|
|
3511
|
+
class="send"
|
|
3453
3512
|
?disabled=${this.busy || !!this.sendBlocked || (!this.input.trim() && this.attachments.length === 0)}
|
|
3454
3513
|
@click=${() => this.submitMessage()}
|
|
3455
3514
|
aria-label=${i18next.t(this.addressesPeople ? 'ai-assistant.text.send-to-people' : 'ai-assistant.text.send-to-ai')}
|
|
@@ -3461,6 +3520,17 @@ export class OxAssistantChat extends LitElement {
|
|
|
3461
3520
|
? html`<span class="spinner"></span>`
|
|
3462
3521
|
: html`<md-icon>arrow_upward</md-icon>`}
|
|
3463
3522
|
</button>
|
|
3523
|
+
${this.acceptsAttachments
|
|
3524
|
+
? html`<button
|
|
3525
|
+
type="button"
|
|
3526
|
+
class="attach"
|
|
3527
|
+
aria-label=${i18next.t('ai-assistant.button.attach', { defaultValue: 'Attach' })}
|
|
3528
|
+
title=${i18next.t('ai-assistant.tooltip.attach-file', { defaultValue: 'attach an image (drag & drop or paste supported)' })}
|
|
3529
|
+
@click=${() => this.renderRoot.querySelector<HTMLInputElement>('#chat-file-input')?.click()}>
|
|
3530
|
+
<md-icon>attach_file</md-icon>
|
|
3531
|
+
</button>`
|
|
3532
|
+
: nothing}
|
|
3533
|
+
</div>
|
|
3464
3534
|
</div>
|
|
3465
3535
|
</div>
|
|
3466
3536
|
`
|
|
@@ -3691,8 +3761,13 @@ export class OxAssistantChat extends LitElement {
|
|
|
3691
3761
|
return
|
|
3692
3762
|
}
|
|
3693
3763
|
}
|
|
3694
|
-
const action = composerKeyAction(e, this.composerWide)
|
|
3695
|
-
if (action === '
|
|
3764
|
+
const action = composerKeyAction(e, this.composerWide, { empty: !this.input, suggestion: this.suggestion })
|
|
3765
|
+
if (action === 'take-suggestion') {
|
|
3766
|
+
e.preventDefault()
|
|
3767
|
+
this.input = this.suggestion!
|
|
3768
|
+
const ta = e.target as HTMLTextAreaElement
|
|
3769
|
+
this.updateComplete.then(() => ta.setSelectionRange(ta.value.length, ta.value.length))
|
|
3770
|
+
} else if (action === 'narrow') {
|
|
3696
3771
|
/* The Escape that narrows the box is spent here - the floating dock closes on the next one. */
|
|
3697
3772
|
e.preventDefault()
|
|
3698
3773
|
e.stopPropagation()
|
|
@@ -3703,6 +3778,11 @@ export class OxAssistantChat extends LitElement {
|
|
|
3703
3778
|
}
|
|
3704
3779
|
}
|
|
3705
3780
|
|
|
3781
|
+
/** The request a Tab writes into the empty box - `composerSuggestion` decides it. */
|
|
3782
|
+
private get suggestion(): string | undefined {
|
|
3783
|
+
return composerSuggestion({ started: this.lines.length > 0, modeExamples: this.modeView?.examples, hostExamples: this.examples })
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3706
3786
|
/** input 이벤트 — value 즉시 반영 + popup 갱신은 150ms 디바운스 (F-Finder 와 동일).
|
|
3707
3787
|
* big board 에서 매 keystroke filter 비용 절감 + 한국어 IME 조합 중 깜빡임 완화. */
|
|
3708
3788
|
private onTextareaInput(e: Event) {
|
|
@@ -4003,15 +4083,6 @@ export class OxAssistantChat extends LitElement {
|
|
|
4003
4083
|
}))
|
|
4004
4084
|
}
|
|
4005
4085
|
|
|
4006
|
-
/** 이 표면이 실제로 쓸 수 있는 트리거 — 판정은 `chat-triggers.ts` 가 하고 여기서는 사실만 읽는다. */
|
|
4007
|
-
private get triggerChips(): TriggerChip[] {
|
|
4008
|
-
return availableTriggers({
|
|
4009
|
-
hasMentionableThings: !!(this.boardProvider || this.currentBoard || this.knownTypes?.length),
|
|
4010
|
-
hasSession: !!this.sessionId,
|
|
4011
|
-
slashTemplateCount: this.conversation.slashTemplates.length
|
|
4012
|
-
})
|
|
4013
|
-
}
|
|
4014
|
-
|
|
4015
4086
|
private static readonly EXAMPLE_GROUPS: Array<{
|
|
4016
4087
|
labelKey: string
|
|
4017
4088
|
itemKeys: string[]
|
|
@@ -4065,12 +4136,34 @@ export class OxAssistantChat extends LitElement {
|
|
|
4065
4136
|
*/
|
|
4066
4137
|
private renderModeSubject() {
|
|
4067
4138
|
if (this.modeChoices.length < 2) return nothing
|
|
4139
|
+
return html`<div class="mode-subject">${this.renderModePicker()}</div>`
|
|
4140
|
+
}
|
|
4068
4141
|
|
|
4142
|
+
/**
|
|
4143
|
+
* Which AI answers the next turn - at the end of the row above the box, where the words about writing stand. A row of
|
|
4144
|
+
* its own spent a line of the dock on one control. It is a property of the turn, not of the conversation: switching
|
|
4145
|
+
* keeps the conversation and only the next answer comes from the other agent. Drawn only when there is a choice.
|
|
4146
|
+
*/
|
|
4147
|
+
private renderAgentPicker() {
|
|
4148
|
+
if (!offersAgentPicker(this.agentChoices)) return nothing
|
|
4149
|
+
const current = this.agentChoices.find(one => one.id === this.agentId)
|
|
4150
|
+
return html`
|
|
4151
|
+
<select
|
|
4152
|
+
class="mode-select agent-select"
|
|
4153
|
+
aria-label=${i18next.t('ai-assistant.label.ai-agent', { defaultValue: 'AI agent' })}
|
|
4154
|
+
title=${current ? agentDetail(current) : ''}
|
|
4155
|
+
?disabled=${this.busy}
|
|
4156
|
+
@change=${(event: Event) => this.onAgentPick((event.target as HTMLSelectElement).value)}>
|
|
4157
|
+
${this.agentChoices.map(one => html`<option value=${one.id} ?selected=${one.id === this.agentId}>${one.name}</option>`)}
|
|
4158
|
+
</select>
|
|
4159
|
+
`
|
|
4160
|
+
}
|
|
4161
|
+
|
|
4162
|
+
private renderModePicker() {
|
|
4069
4163
|
const current = this.modeChoices.find(choice => choice.kind === this.mode?.kind)
|
|
4070
4164
|
const others = this.modeChoices.filter(choice => choice.kind !== this.mode?.kind)
|
|
4071
4165
|
|
|
4072
4166
|
return html`
|
|
4073
|
-
<div class="mode-subject">
|
|
4074
4167
|
<span class="mode-now">${current?.label ?? this.mode?.kind ?? ''}</span>
|
|
4075
4168
|
${others.length === 1
|
|
4076
4169
|
? html`
|
|
@@ -4094,7 +4187,6 @@ export class OxAssistantChat extends LitElement {
|
|
|
4094
4187
|
)}
|
|
4095
4188
|
</select>
|
|
4096
4189
|
`}
|
|
4097
|
-
</div>
|
|
4098
4190
|
`
|
|
4099
4191
|
}
|
|
4100
4192
|
|
|
@@ -4166,27 +4258,6 @@ export class OxAssistantChat extends LitElement {
|
|
|
4166
4258
|
</div>
|
|
4167
4259
|
</div>
|
|
4168
4260
|
|
|
4169
|
-
${this.triggerChips.length > 0
|
|
4170
|
-
? html`
|
|
4171
|
-
<div class="trigger-row">
|
|
4172
|
-
<div class="trigger-row-label">${i18next.t('ai-assistant.text.triggers-heading')}</div>
|
|
4173
|
-
<div class="trigger-chips">
|
|
4174
|
-
${this.triggerChips.map(
|
|
4175
|
-
trigger => html`
|
|
4176
|
-
<button
|
|
4177
|
-
class="trigger-chip"
|
|
4178
|
-
title=${i18next.t(trigger.tooltipKey)}
|
|
4179
|
-
@click=${() => this.insertTrigger(trigger.char)}>
|
|
4180
|
-
<span class="trigger-char">${trigger.char}</span>
|
|
4181
|
-
<span class="trigger-desc">${i18next.t(trigger.labelKey)}</span>
|
|
4182
|
-
</button>
|
|
4183
|
-
`
|
|
4184
|
-
)}
|
|
4185
|
-
</div>
|
|
4186
|
-
</div>
|
|
4187
|
-
`
|
|
4188
|
-
: ''}
|
|
4189
|
-
|
|
4190
4261
|
${this.exampleGroups.map(
|
|
4191
4262
|
group => html`
|
|
4192
4263
|
<div class="group">
|
|
@@ -4202,33 +4273,11 @@ export class OxAssistantChat extends LitElement {
|
|
|
4202
4273
|
`
|
|
4203
4274
|
)}
|
|
4204
4275
|
|
|
4205
|
-
|
|
4206
|
-
<span class="badge">${i18next.t('ai-assistant.label.korean-supported')}</span>
|
|
4207
|
-
<span class="badge">${i18next.t('ai-assistant.label.multi-command')}</span>
|
|
4208
|
-
<span class="badge">${i18next.t('ai-assistant.label.review-able')}</span>
|
|
4209
|
-
${this.conversation.footerNotice
|
|
4210
|
-
? html`<br />${this.conversation.footerNotice}`
|
|
4211
|
-
: ''}
|
|
4212
|
-
</div>
|
|
4276
|
+
${this.conversation.footerNotice ? html`<div class="footer">${this.conversation.footerNotice}</div>` : ''}
|
|
4213
4277
|
</div>
|
|
4214
4278
|
`
|
|
4215
4279
|
}
|
|
4216
4280
|
|
|
4217
|
-
/** trigger chip 클릭 — textarea 에 trigger 문자 삽입 + focus + popup 자동 활성. */
|
|
4218
|
-
private insertTrigger(trigger: '#' | '@' | '/') {
|
|
4219
|
-
// 입력 끝에 공백이 없으면 공백 추가 후 trigger — 자연스러운 위치
|
|
4220
|
-
const needsSpace = this.input.length > 0 && !/\s$/.test(this.input)
|
|
4221
|
-
this.input = this.input + (needsSpace ? ' ' : '') + trigger
|
|
4222
|
-
requestAnimationFrame(() => {
|
|
4223
|
-
const ta = this.renderRoot.querySelector('textarea') as HTMLTextAreaElement | null
|
|
4224
|
-
if (!ta) return
|
|
4225
|
-
ta.focus()
|
|
4226
|
-
ta.setSelectionRange(ta.value.length, ta.value.length)
|
|
4227
|
-
// popup refresh — onTextareaInput 이 디바운스 후 호출되지만 직접 호출로 즉각 반영
|
|
4228
|
-
this.refreshMentionPopup(ta)
|
|
4229
|
-
})
|
|
4230
|
-
}
|
|
4231
|
-
|
|
4232
4281
|
/** 예시 클릭 — 입력창에 채우고 포커스 (사용자가 수정 후 전송 가능) */
|
|
4233
4282
|
private useExample(text: string) {
|
|
4234
4283
|
this.input = text
|
|
@@ -4941,8 +4990,8 @@ export class OxAssistantChat extends LitElement {
|
|
|
4941
4990
|
try {
|
|
4942
4991
|
// 클립보드에는 marker 제거된 깨끗한 텍스트
|
|
4943
4992
|
await navigator.clipboard.writeText(stripMentionRefids(content))
|
|
4993
|
+
/* The button turns into a check where it was pressed - that is the whole answer. */
|
|
4944
4994
|
this.copiedIdx = idx
|
|
4945
|
-
this.showToast(i18next.t('ai-assistant.text.copied'))
|
|
4946
4995
|
setTimeout(() => {
|
|
4947
4996
|
if (this.copiedIdx === idx) this.copiedIdx = undefined
|
|
4948
4997
|
}, 1500)
|
|
@@ -5168,7 +5217,8 @@ export class OxAssistantChat extends LitElement {
|
|
|
5168
5217
|
mode: { kind: this.conversation.kind!, target: this.conversation.target },
|
|
5169
5218
|
truncateAfterMessageId: this._truncateAfterId,
|
|
5170
5219
|
hostContext: requestContext.hostContext,
|
|
5171
|
-
attachmentIds
|
|
5220
|
+
attachmentIds,
|
|
5221
|
+
agentId: this.agentId
|
|
5172
5222
|
})
|
|
5173
5223
|
: buildChatMutationInput({
|
|
5174
5224
|
sessionId: this.sessionId,
|
|
@@ -5187,7 +5237,8 @@ export class OxAssistantChat extends LitElement {
|
|
|
5187
5237
|
selectedRefids: this.selectedRefids ?? [],
|
|
5188
5238
|
mentions,
|
|
5189
5239
|
/* With a declared mode the server owns persona and tools; the board door still carries the board. */
|
|
5190
|
-
mode: this.conversation.kind ? { kind: this.conversation.kind, target: this.conversation.target } : undefined
|
|
5240
|
+
mode: this.conversation.kind ? { kind: this.conversation.kind, target: this.conversation.target } : undefined,
|
|
5241
|
+
agentId: this.agentId
|
|
5191
5242
|
})
|
|
5192
5243
|
}
|
|
5193
5244
|
})
|