@things-factory/ai-assistant 10.1.154 → 10.1.156
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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { findMenu, foldForDock, unfoldAfterDock } from '../utils/menu-room.js'
|
|
1
2
|
import '@material/web/icon/icon.js'
|
|
2
3
|
import './assistant-chat.js'
|
|
3
4
|
import './assistant-session-toolbar.js'
|
|
@@ -105,13 +106,17 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
105
106
|
font-size: 13px;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
/*
|
|
109
|
-
|
|
109
|
+
/*
|
|
110
|
+
* The dock opens in one place on every screen and takes its room beside the page, so it covers no page's controls
|
|
111
|
+
* and no screen needs its own exception (2026-10-12). Only a screen too narrow to share floats it over the page.
|
|
112
|
+
* The DOM stays - the chat does not reconnect.
|
|
113
|
+
*/
|
|
114
|
+
@media (max-width: 1023.98px) {
|
|
110
115
|
:host {
|
|
111
116
|
position: fixed;
|
|
112
|
-
inset:
|
|
113
|
-
height:
|
|
114
|
-
height:
|
|
117
|
+
inset: 0 0 0 auto;
|
|
118
|
+
height: 100vh;
|
|
119
|
+
height: 100dvh;
|
|
115
120
|
z-index: 10;
|
|
116
121
|
box-shadow: var(--md-sys-elevation-shadow-3, -4px 0 16px rgb(0 0 0 / 20%));
|
|
117
122
|
}
|
|
@@ -278,8 +283,6 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
278
283
|
.next-step a:hover { text-decoration: underline; }
|
|
279
284
|
.next-step md-icon { --md-icon-size: 16px; }
|
|
280
285
|
ox-assistant-chat[hidden] { display: none; }
|
|
281
|
-
.transition { padding: 10px 12px; font-size: 13px; line-height: 1.5; border-bottom: 1px solid var(--md-sys-color-outline-variant); background: var(--md-sys-color-surface-container-low); }
|
|
282
|
-
.transition button { display: block; margin-top: 6px; font: inherit; }
|
|
283
286
|
`
|
|
284
287
|
]
|
|
285
288
|
|
|
@@ -292,7 +295,6 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
292
295
|
@state() private anchor?: AnchorResult
|
|
293
296
|
@state() private kind = ''
|
|
294
297
|
/** kind → its label, for the manager's rows. */
|
|
295
|
-
@state() private kindLabels: Record<string, string> = {}
|
|
296
298
|
@state() private sessions: AssistantSession[] = []
|
|
297
299
|
@state() private sessionId = ''
|
|
298
300
|
@state() private loading = false
|
|
@@ -300,8 +302,6 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
300
302
|
@state() private showingHistory = false
|
|
301
303
|
@state() private conversationView = 0
|
|
302
304
|
@state() private asking?: { ask?: string; draft?: string; requestId: string }
|
|
303
|
-
@state() private screenChanged = false
|
|
304
|
-
@state() private navigationNotice = ''
|
|
305
305
|
private openedIdentity = ''
|
|
306
306
|
|
|
307
307
|
private app = new AppController(this)
|
|
@@ -329,22 +329,6 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
329
329
|
|
|
330
330
|
private onStartNewSession = () => void this.newConversation()
|
|
331
331
|
|
|
332
|
-
/** An editor can reserve its toolbar above the floating dock. Other apps keep the full-height default. */
|
|
333
|
-
@property({ attribute: false }) overlayBoundary?: () => Element | undefined
|
|
334
|
-
private boundary?: Element
|
|
335
|
-
private boundaryObserver?: ResizeObserver
|
|
336
|
-
private placeOverlay = () => {
|
|
337
|
-
const boundary = this.overlayBoundary?.()
|
|
338
|
-
if (boundary !== this.boundary) {
|
|
339
|
-
this.boundaryObserver?.disconnect()
|
|
340
|
-
this.boundary = boundary
|
|
341
|
-
if (boundary) {
|
|
342
|
-
this.boundaryObserver = new ResizeObserver(this.placeOverlay)
|
|
343
|
-
this.boundaryObserver.observe(boundary)
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
this.style.setProperty('--assistant-dock-overlay-top', `${boundary?.getBoundingClientRect().bottom ?? 0}px`)
|
|
347
|
-
}
|
|
348
332
|
|
|
349
333
|
/* A card asks for a page of this application - the AI 작업판. The dock stays open beside it. */
|
|
350
334
|
private onOpenPage = (event: CustomEvent) => {
|
|
@@ -364,10 +348,13 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
364
348
|
else this.removeAttribute('audience')
|
|
365
349
|
}
|
|
366
350
|
|
|
351
|
+
/* Whether the left menu was folded by this dock's opening - only then does closing open it again. */
|
|
352
|
+
private menuFoldedByDock = false
|
|
353
|
+
|
|
367
354
|
connectedCallback() {
|
|
368
355
|
super.connectedCallback()
|
|
369
|
-
|
|
370
|
-
this.
|
|
356
|
+
this.menuFoldedByDock = foldForDock(findMenu())
|
|
357
|
+
if (this.menuFoldedByDock) window.dispatchEvent(new Event('resize'))
|
|
371
358
|
this.addEventListener('assistant-start-new-session', this.onStartNewSession)
|
|
372
359
|
this.addEventListener('assistant-open-page', this.onOpenPage as EventListener)
|
|
373
360
|
this.unsubscribers.push(
|
|
@@ -390,9 +377,9 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
390
377
|
|
|
391
378
|
/* Closing keeps the conversation: the dock comes back as it was left (design/ai/shared-dock.md §2-1, §2-7). */
|
|
392
379
|
disconnectedCallback() {
|
|
393
|
-
|
|
394
|
-
this.
|
|
395
|
-
this.
|
|
380
|
+
unfoldAfterDock(findMenu(), this.menuFoldedByDock)
|
|
381
|
+
if (this.menuFoldedByDock) window.dispatchEvent(new Event('resize'))
|
|
382
|
+
this.menuFoldedByDock = false
|
|
396
383
|
this.removeEventListener('assistant-start-new-session', this.onStartNewSession)
|
|
397
384
|
this.removeEventListener('assistant-open-page', this.onOpenPage as EventListener)
|
|
398
385
|
this.unsubscribers.splice(0).forEach(unsubscribe => unsubscribe())
|
|
@@ -433,43 +420,66 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
433
420
|
if (!(await this.mayLeaveConversation())) return
|
|
434
421
|
this.chosenKind = ''
|
|
435
422
|
this.surface = surface
|
|
436
|
-
this.navigationNotice = surface ? t('now-about', 'Conversation subject: {name}', { name: surface.label() }) : ''
|
|
437
423
|
await this.open()
|
|
438
424
|
}
|
|
439
425
|
|
|
440
426
|
/** The screen changed - a different surface or domain is a different conversation. */
|
|
441
427
|
private follow(force = false) {
|
|
442
|
-
this.placeOverlay()
|
|
443
428
|
const next = currentAssistantSurface()
|
|
444
429
|
const transition = surfaceTransition({
|
|
445
430
|
identityChanged: this.openedIdentity !== this.identityKey(),
|
|
446
431
|
sameSubject: this.scopeKey(next) === this.openedFor,
|
|
447
|
-
|
|
432
|
+
busy: this.navigationState.busy
|
|
448
433
|
})
|
|
449
434
|
if (!force && transition === 'refresh') {
|
|
450
435
|
this.surface = next
|
|
451
|
-
this.screenChanged = false
|
|
452
436
|
this.requestUpdate()
|
|
453
437
|
return
|
|
454
438
|
}
|
|
455
|
-
if (!force && transition === '
|
|
456
|
-
|
|
439
|
+
if (!force && transition === 'wait') {
|
|
440
|
+
/* The answer lands in the conversation it was asked in; then the dock follows the page. */
|
|
441
|
+
this.followWhenIdle()
|
|
457
442
|
return
|
|
458
443
|
}
|
|
444
|
+
if (transition !== 'reset') this.keepDraft()
|
|
459
445
|
if (transition === 'reset') {
|
|
460
446
|
this.asking = undefined
|
|
461
447
|
this.capability = 'unknown'
|
|
462
448
|
if (this.openedIdentity) takeAssistantAsk()
|
|
463
449
|
}
|
|
464
|
-
this.navigationNotice = transition === 'follow' && next ? t('now-about', 'Conversation subject: {name}', { name: next.label() }) : ''
|
|
465
450
|
if (next?.key !== this.surface?.key) this.chosenKind = ''
|
|
466
451
|
this.surface = next
|
|
467
452
|
void this.open()
|
|
468
453
|
}
|
|
469
454
|
|
|
455
|
+
private followTimer?: ReturnType<typeof setTimeout>
|
|
456
|
+
|
|
457
|
+
private followWhenIdle() {
|
|
458
|
+
clearTimeout(this.followTimer)
|
|
459
|
+
this.followTimer = setTimeout(() => (this.navigationState.busy ? this.followWhenIdle() : this.follow()), 300)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* What was being written, per subject - it goes back to its conversation when the page returns there. */
|
|
463
|
+
private drafts = new Map<string, unknown>()
|
|
464
|
+
|
|
465
|
+
private keepDraft() {
|
|
466
|
+
const chat = this.renderRoot.querySelector('ox-assistant-chat') as any
|
|
467
|
+
const draft = this.openedFor ? chat?.takeDraft?.() : undefined
|
|
468
|
+
if (draft) this.drafts.set(this.openedFor!, draft)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private async restoreDraft() {
|
|
472
|
+
const draft = this.openedFor ? this.drafts.get(this.openedFor) : undefined
|
|
473
|
+
if (!draft) return
|
|
474
|
+
await this.updateComplete
|
|
475
|
+
const chat = this.renderRoot.querySelector('ox-assistant-chat') as any
|
|
476
|
+
if (!chat?.restoreDraft) return
|
|
477
|
+
this.drafts.delete(this.openedFor!)
|
|
478
|
+
chat.restoreDraft(draft)
|
|
479
|
+
}
|
|
480
|
+
|
|
470
481
|
private async open(): Promise<void> {
|
|
471
482
|
this.openedIdentity = this.identityKey()
|
|
472
|
-
this.screenChanged = false
|
|
473
483
|
const ticket = ++this.revision
|
|
474
484
|
this.conversationView++
|
|
475
485
|
this.showingHistory = false
|
|
@@ -496,7 +506,6 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
496
506
|
|
|
497
507
|
const modes = await modesFor(surface.subject)
|
|
498
508
|
if (ticket !== this.revision) return
|
|
499
|
-
this.kindLabels = Object.fromEntries(modes.map(mode => [mode.kind, mode.label || mode.kind]))
|
|
500
509
|
const chosen = modes.find(mode => mode.kind === this.chosenKind) ?? modes.find(mode => mode.isDefault) ?? modes[0]
|
|
501
510
|
this.kind = chosen?.kind ?? ''
|
|
502
511
|
if (!this.kind) {
|
|
@@ -517,6 +526,8 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
517
526
|
this.loading = false
|
|
518
527
|
/* No conversation can stand here - an ask waiting for one is answered now, not sent later somewhere else. */
|
|
519
528
|
if (this.anchor && !isAnchor(this.anchor)) void this.takeAsk()
|
|
529
|
+
/* What was being written on this subject before the page moved away comes back here. */
|
|
530
|
+
void this.restoreDraft()
|
|
520
531
|
}
|
|
521
532
|
}
|
|
522
533
|
}
|
|
@@ -566,7 +577,7 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
566
577
|
|
|
567
578
|
private async takeAsk() {
|
|
568
579
|
if (this.loading || !this.anchor) return
|
|
569
|
-
if (this.
|
|
580
|
+
if (this.navigationState.busy || this.navigationState.draft) {
|
|
570
581
|
const waiting = takeAssistantAsk()
|
|
571
582
|
if (waiting) notify({ level: 'info', message: t('ask-kept-current', 'The current conversation was kept. Switch to the intended subject before asking again.') })
|
|
572
583
|
return
|
|
@@ -818,18 +829,13 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
818
829
|
<span>${surface.nextStep.description?.() || ''}</span>
|
|
819
830
|
<a href=${surface.nextStep.href}>${surface.nextStep.label()}<md-icon>arrow_forward</md-icon></a>
|
|
820
831
|
</div>` : nothing}
|
|
821
|
-
|
|
822
|
-
${t('screen-and-conversation', 'Screen: {screen}. Conversation: {conversation}. Your conversation has been kept.', { screen: currentAssistantSurface()?.label() || t('no-subject', 'No subject'), conversation: surface?.label() || '' })}
|
|
823
|
-
<button class="sbtn" @click=${() => this.requestSurface(currentAssistantSurface())}>${t('switch-to-screen', 'Talk about this screen')}</button>
|
|
824
|
-
</div>` : this.navigationNotice ? html`<div class="transition" role="status">${this.navigationNotice}</div>` : nothing}
|
|
832
|
+
|
|
825
833
|
${ready && this.showingHistory
|
|
826
834
|
? html`<ox-assistant-session-manager
|
|
827
835
|
.anchorType=${(anchor as any).anchorType}
|
|
828
836
|
.anchorId=${(anchor as any).anchorId}
|
|
829
837
|
.sessionId=${this.sessionId}
|
|
830
838
|
.kind=${this.kind}
|
|
831
|
-
.kindLabels=${this.kindLabels}
|
|
832
|
-
.scopeLabel=${t('conversation-scope', 'Conversations · {name}', { name: surface?.label() || '' })}
|
|
833
839
|
@assistant-session-select=${(event: CustomEvent) => this.selectSession(event.detail.sessionId, event.detail.kind)}
|
|
834
840
|
@assistant-session-reset=${() => this.newConversation()}
|
|
835
841
|
@assistant-session-changed=${() => this.reloadSessions()}
|
|
@@ -860,7 +866,6 @@ export class AssistantDock extends localize(i18next)(LitElement) {
|
|
|
860
866
|
this.conversationView,
|
|
861
867
|
html`<ox-assistant-chat
|
|
862
868
|
?hidden=${this.showingHistory}
|
|
863
|
-
.sendBlocked=${this.screenChanged ? t('return-to-subject', 'Return to the conversation subject before sending or applying changes.') : ''}
|
|
864
869
|
.sessionId=${this.sessionId}
|
|
865
870
|
.prepareSession=${(adopt: (id: string) => void) => this.controller.prepareSession(adopt)}
|
|
866
871
|
.contextProvider=${() => {
|
|
@@ -5,6 +5,8 @@ import { customElement, property, state } from 'lit/decorators.js'
|
|
|
5
5
|
|
|
6
6
|
import { i18next } from '@operato/i18n'
|
|
7
7
|
import { OxPrompt } from '@operato/popup'
|
|
8
|
+
import '@operato/popup/ox-popup-menu.js'
|
|
9
|
+
import { OxPopupMenu } from '@operato/popup/ox-popup-menu.js'
|
|
8
10
|
|
|
9
11
|
import {
|
|
10
12
|
assistantSessionAdmin,
|
|
@@ -47,29 +49,25 @@ const t = (key: string, defaultValue: string, options: Record<string, unknown> =
|
|
|
47
49
|
@customElement('ox-assistant-session-manager')
|
|
48
50
|
export class AssistantSessionManager extends LitElement {
|
|
49
51
|
static styles = css`
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
.
|
|
53
|
-
.filters[hidden] { display: none; }
|
|
54
|
-
.open-row { display: block; flex: 1; min-width: 0; text-align: start; border: 0; background: transparent; color: inherit; font: inherit; padding: 4px; cursor: pointer; }
|
|
55
|
-
.meta { display: block; margin-top: 5px; font-size: 12px; color: var(--md-sys-color-on-surface-variant); }
|
|
52
|
+
/* One scale for the list: name 13, preview 12, meta 11 - the rest of the dock's sizes. */
|
|
53
|
+
.open-row { display: block; flex: 1; min-width: 0; text-align: start; border: 0; background: transparent; color: inherit; font: inherit; padding: 0; cursor: pointer; }
|
|
54
|
+
.meta { display: block; margin-top: 2px; font-size: 11px; color: var(--md-sys-color-on-surface-variant); }
|
|
56
55
|
/* Open and private must part at a glance - an open row is read by everyone in the domain (2026-09-30). */
|
|
57
56
|
.audience { display: inline-flex; align-items: center; gap: 3px; vertical-align: middle; }
|
|
58
57
|
.audience md-icon { --md-icon-size: 14px; }
|
|
59
58
|
/* The dock's warning colour, fixed across applications - see assistant-dock.ts. */
|
|
60
59
|
:host { --assistant-open-edge: #c47f00; --assistant-open-surface: #f2b01e; --assistant-open-ink: #2e2100; }
|
|
61
|
-
.audience.open { padding:
|
|
60
|
+
.audience.open { padding: 0 5px 0 3px; border-radius: 4px; background: var(--assistant-open-surface); color: var(--assistant-open-ink); font-weight: 600; }
|
|
61
|
+
.audience md-icon { --md-icon-size: 12px; }
|
|
62
62
|
.row.open-audience { box-shadow: inset 4px 0 0 var(--assistant-open-edge); }
|
|
63
|
-
.
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
.actions button.danger { color: var(--md-sys-color-error, #b42318); }
|
|
67
|
-
button:focus-visible, summary:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 2px; }
|
|
63
|
+
.more-menu { flex: none; border: 0; border-radius: 4px; padding: 2px 6px; background: none; color: var(--md-sys-color-on-surface-variant); font-size: 14px; line-height: 1; cursor: pointer; }
|
|
64
|
+
.more-menu:hover { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }
|
|
65
|
+
button:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 2px; }
|
|
68
66
|
:host {
|
|
69
67
|
display: flex;
|
|
70
68
|
flex-direction: column;
|
|
71
69
|
min-height: 0;
|
|
72
|
-
font-size:
|
|
70
|
+
font-size: 13px;
|
|
73
71
|
color: var(--md-sys-color-on-surface);
|
|
74
72
|
}
|
|
75
73
|
|
|
@@ -77,14 +75,14 @@ export class AssistantSessionManager extends LitElement {
|
|
|
77
75
|
display: flex;
|
|
78
76
|
align-items: center;
|
|
79
77
|
gap: 6px;
|
|
80
|
-
padding: 8px 10px;
|
|
81
|
-
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
78
|
+
padding: 8px 10px 6px;
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
.bar input,
|
|
85
82
|
.bar select {
|
|
86
83
|
min-width: 0;
|
|
87
|
-
height:
|
|
84
|
+
height: 26px;
|
|
85
|
+
font-size: 12px;
|
|
88
86
|
box-sizing: border-box;
|
|
89
87
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
90
88
|
border-radius: 6px;
|
|
@@ -106,28 +104,32 @@ export class AssistantSessionManager extends LitElement {
|
|
|
106
104
|
background: transparent;
|
|
107
105
|
color: var(--md-sys-color-primary);
|
|
108
106
|
font: inherit;
|
|
109
|
-
font-
|
|
107
|
+
font-size: 12px;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
white-space: nowrap;
|
|
110
110
|
cursor: pointer;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/* Always open, one quiet line under the search - the same small size as the search. */
|
|
113
114
|
.filters {
|
|
114
115
|
display: flex;
|
|
115
116
|
flex-wrap: wrap;
|
|
116
117
|
align-items: center;
|
|
117
|
-
gap:
|
|
118
|
-
padding:
|
|
118
|
+
gap: 4px 10px;
|
|
119
|
+
padding: 0 10px 8px;
|
|
119
120
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
120
|
-
font-size:
|
|
121
|
+
font-size: 11px;
|
|
121
122
|
color: var(--md-sys-color-on-surface-variant);
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
.filters select {
|
|
125
|
-
height:
|
|
126
|
+
height: 22px;
|
|
126
127
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
127
|
-
border-radius:
|
|
128
|
+
border-radius: 4px;
|
|
128
129
|
background: var(--md-sys-color-surface-container-lowest);
|
|
129
130
|
color: inherit;
|
|
130
131
|
font: inherit;
|
|
132
|
+
padding: 0 4px;
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
.filters label {
|
|
@@ -137,8 +139,18 @@ export class AssistantSessionManager extends LitElement {
|
|
|
137
139
|
cursor: pointer;
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
.
|
|
142
|
+
.filters input[type='checkbox'] {
|
|
143
|
+
margin: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.filters .clear {
|
|
147
|
+
margin-left: auto;
|
|
148
|
+
border: 0;
|
|
149
|
+
background: none;
|
|
150
|
+
padding: 0;
|
|
141
151
|
color: var(--md-sys-color-primary);
|
|
152
|
+
font: inherit;
|
|
153
|
+
cursor: pointer;
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
.list {
|
|
@@ -152,7 +164,7 @@ export class AssistantSessionManager extends LitElement {
|
|
|
152
164
|
display: flex;
|
|
153
165
|
align-items: center;
|
|
154
166
|
gap: 8px;
|
|
155
|
-
padding:
|
|
167
|
+
padding: 7px 10px;
|
|
156
168
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
157
169
|
cursor: pointer;
|
|
158
170
|
}
|
|
@@ -200,33 +212,9 @@ export class AssistantSessionManager extends LitElement {
|
|
|
200
212
|
}
|
|
201
213
|
|
|
202
214
|
.prev {
|
|
215
|
+
margin-top: 1px;
|
|
203
216
|
color: var(--md-sys-color-on-surface-variant);
|
|
204
|
-
font-size:
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/* Row actions show on hover and on keyboard focus - the list's body is the names. */
|
|
208
|
-
.acts {
|
|
209
|
-
display: none;
|
|
210
|
-
gap: 2px;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
button.icon {
|
|
214
|
-
display: inline-flex;
|
|
215
|
-
padding: 3px;
|
|
216
|
-
border: 0;
|
|
217
|
-
border-radius: 6px;
|
|
218
|
-
background: none;
|
|
219
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
220
|
-
cursor: pointer;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
button.icon:hover {
|
|
224
|
-
background: var(--md-sys-color-surface-container-high);
|
|
225
|
-
color: var(--md-sys-color-on-surface);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
button.icon.danger:hover {
|
|
229
|
-
color: var(--md-sys-color-error);
|
|
217
|
+
font-size: 12px;
|
|
230
218
|
}
|
|
231
219
|
|
|
232
220
|
.rename {
|
|
@@ -313,19 +301,15 @@ export class AssistantSessionManager extends LitElement {
|
|
|
313
301
|
@property({ type: String }) anchorId = ''
|
|
314
302
|
/** The conversation the dock has open - marked in the list. */
|
|
315
303
|
@property({ type: String }) sessionId = ''
|
|
316
|
-
@property({ type: String }) scopeLabel = ''
|
|
317
304
|
/** The doors. Replaceable for tests; a host normally leaves it. */
|
|
318
305
|
@property({ attribute: false }) admin: AssistantSessionAdmin = assistantSessionAdmin
|
|
319
306
|
/** The conversation kind (mode) open in the dock - the list's default scope (§2-6). */
|
|
320
307
|
@property({ type: String }) kind = ''
|
|
321
|
-
/** kind → the words people know it by (the modes' labels), for each row. */
|
|
322
|
-
@property({ attribute: false }) kindLabels: Record<string, string> = {}
|
|
323
308
|
|
|
324
309
|
@state() private entries: AssistantSessionEntry[] = []
|
|
325
310
|
@state() private total = 0
|
|
326
311
|
@state() private hiddenCount = 0
|
|
327
312
|
@state() private search = ''
|
|
328
|
-
@state() private allKinds = false
|
|
329
313
|
@state() private whose: NonNullable<AssistantSessionFilters['whose']> = 'all'
|
|
330
314
|
@state() private visibility: NonNullable<AssistantSessionFilters['visibility']> = 'all'
|
|
331
315
|
@state() private includeHidden = false
|
|
@@ -334,17 +318,20 @@ export class AssistantSessionManager extends LitElement {
|
|
|
334
318
|
@state() private loading = false
|
|
335
319
|
@state() private error = ''
|
|
336
320
|
@state() private renamingId = ''
|
|
337
|
-
@state() private filtersOpen = false
|
|
338
321
|
|
|
339
|
-
|
|
340
|
-
|
|
322
|
+
/*
|
|
323
|
+
* The search and the filters stand open together, always (2026-10-12) - a filter behind a "Filters" button is one
|
|
324
|
+
* more thing to find before finding the conversation. The list is this screen's conversations, so there is no kind
|
|
325
|
+
* to choose.
|
|
326
|
+
*/
|
|
327
|
+
private get filtering() {
|
|
328
|
+
return !!this.search.trim() || this.whose !== 'all' || this.visibility !== 'all' || this.includeHidden || this.unreadOnly
|
|
341
329
|
}
|
|
342
330
|
|
|
343
|
-
private
|
|
331
|
+
private clearFilters() {
|
|
344
332
|
clearTimeout(this.searchTimer)
|
|
345
333
|
this.refilter(() => {
|
|
346
334
|
this.search = ''
|
|
347
|
-
this.allKinds = false
|
|
348
335
|
this.whose = 'all'
|
|
349
336
|
this.visibility = 'all'
|
|
350
337
|
this.includeHidden = this.unreadOnly = false
|
|
@@ -375,7 +362,7 @@ export class AssistantSessionManager extends LitElement {
|
|
|
375
362
|
anchorId: this.anchorId,
|
|
376
363
|
search: this.search,
|
|
377
364
|
filters: {
|
|
378
|
-
kind: this.
|
|
365
|
+
kind: this.kind,
|
|
379
366
|
whose: this.whose,
|
|
380
367
|
visibility: this.visibility,
|
|
381
368
|
includeHidden: this.includeHidden,
|
|
@@ -491,7 +478,6 @@ export class AssistantSessionManager extends LitElement {
|
|
|
491
478
|
const shown = this.entries.length
|
|
492
479
|
|
|
493
480
|
return html`
|
|
494
|
-
<div class="scope-label">${this.scopeLabel}</div>
|
|
495
481
|
<div class="bar">
|
|
496
482
|
<input
|
|
497
483
|
.value=${this.search}
|
|
@@ -502,15 +488,7 @@ export class AssistantSessionManager extends LitElement {
|
|
|
502
488
|
/>
|
|
503
489
|
<button class="new" @click=${() => this.emit('assistant-session-reset')}>+ ${t('new', 'New conversation')}</button>
|
|
504
490
|
</div>
|
|
505
|
-
<
|
|
506
|
-
<div class="filters" ?hidden=${!this.filtersOpen}>
|
|
507
|
-
${this.kind
|
|
508
|
-
? html`<select aria-label=${t('filter-kind', 'Which kind')} .value=${this.allKinds ? 'all' : 'this'}
|
|
509
|
-
@change=${(e: Event) => this.refilter(() => (this.allKinds = (e.target as HTMLSelectElement).value === 'all'))}>
|
|
510
|
-
<option value="this">${this.kindLabels[this.kind] || t('filter-this-kind', 'This conversation')}</option>
|
|
511
|
-
<option value="all">${t('filter-all-kinds', 'All kinds here')}</option>
|
|
512
|
-
</select>`
|
|
513
|
-
: nothing}
|
|
491
|
+
<div class="filters">
|
|
514
492
|
<select aria-label=${t('filter-whose', 'Whose')} .value=${this.whose}
|
|
515
493
|
@change=${(e: Event) => this.refilter(() => (this.whose = (e.target as HTMLSelectElement).value as any))}>
|
|
516
494
|
<option value="all">${t('filter-all', 'All')}</option>
|
|
@@ -527,13 +505,13 @@ export class AssistantSessionManager extends LitElement {
|
|
|
527
505
|
@change=${(e: Event) => this.refilter(() => (this.unreadOnly = (e.target as HTMLInputElement).checked))} />${t('filter-unread', 'Unread')}</label>
|
|
528
506
|
<label><input type="checkbox" .checked=${this.includeHidden}
|
|
529
507
|
@change=${(e: Event) => this.refilter(() => (this.includeHidden = (e.target as HTMLInputElement).checked))} />${t('filter-hidden', 'Including hidden')}</label>
|
|
508
|
+
${this.filtering ? html`<button class="clear" @click=${() => this.clearFilters()}>${t('reset-filters', 'Clear search and filters')}</button>` : nothing}
|
|
530
509
|
</div>
|
|
531
|
-
${this.hasFilters ? html`<button class="reset" @click=${() => this.resetFilters()}>${t('reset-filters', 'Clear search and filters')}</button>` : nothing}
|
|
532
510
|
<div class="list">
|
|
533
511
|
${this.error ? html`<div class="note" role="alert">${this.error}</div>` : nothing}
|
|
534
512
|
${this.loading ? html`<div class="note" role="status">${t('loading', 'Loading conversations…')}</div>` : nothing}
|
|
535
513
|
${shown === 0 && !this.loading && !this.error
|
|
536
|
-
? html`<div class="empty" role="status">${this.
|
|
514
|
+
? html`<div class="empty" role="status">${this.filtering ? t('filtered-empty', 'No conversations match these filters here.') : t('scope-empty', 'No conversations yet.')}</div>`
|
|
537
515
|
: this.entries.map(entry => this.renderRow(entry))}
|
|
538
516
|
${shown < this.total
|
|
539
517
|
? html`<button class="more" @click=${() => this.loadMore()}>${t('load-more', 'Show more ({rest})', { rest: this.total - shown })}</button>`
|
|
@@ -575,39 +553,43 @@ export class AssistantSessionManager extends LitElement {
|
|
|
575
553
|
<span class="dot"></span>
|
|
576
554
|
<button class="open-row" aria-current=${entry.id === this.sessionId ? 'true' : 'false'} @click=${() => this.emit('assistant-session-select', { sessionId: entry.id, kind: entry.kind })}>
|
|
577
555
|
<div class="title">${name}</div>
|
|
578
|
-
<div class="prev">${
|
|
556
|
+
<div class="prev">${entry.lastMessagePreview || ''}</div>
|
|
579
557
|
<span class="meta"><span class="audience ${isPrivate ? 'private' : 'open'}"><md-icon aria-hidden="true">${isPrivate ? 'lock' : 'public'}</md-icon>${isPrivate ? t('audience-private', 'Participants only') : t('audience-open', 'Everyone in this domain')}</span>${entry.lastMessageAt && Number.isFinite(Date.parse(entry.lastMessageAt)) ? html` · <time datetime=${entry.lastMessageAt}>${new Intl.DateTimeFormat(i18next.language || 'en', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(entry.lastMessageAt))}</time>` : nothing}${entry.mine ? html` · ${t('filter-mine', 'Started by me')}` : nothing}</span>
|
|
580
558
|
</button>
|
|
581
|
-
<
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
559
|
+
<button
|
|
560
|
+
class="more-menu"
|
|
561
|
+
aria-label=${t('manage', 'Manage conversation')}
|
|
562
|
+
title=${t('manage', 'Manage conversation')}
|
|
563
|
+
@click=${(e: MouseEvent) => this.openRowMenu(e, entry, name)}>⋯</button>
|
|
564
|
+
</div>`
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* A row's actions in the platform's popup menu - one open at a time, closed by a press outside or Escape, placed
|
|
569
|
+
* inside the window. The hand-made menu it replaces stayed open and stacked over the next rows.
|
|
570
|
+
*/
|
|
571
|
+
private openRowMenu(event: MouseEvent, entry: AssistantSessionEntry, name: string) {
|
|
572
|
+
event.stopPropagation()
|
|
573
|
+
const actions = sessionActions(entry)
|
|
574
|
+
const isPrivate = entry.visibility === 'private'
|
|
575
|
+
const at = (event.currentTarget as HTMLElement).getBoundingClientRect()
|
|
576
|
+
OxPopupMenu.open({
|
|
577
|
+
template: html`
|
|
578
|
+
${actions.rename ? html`<ox-popup-menuitem label=${t('rename', 'Rename')} @select=${() => (this.renamingId = entry.id)}></ox-popup-menuitem>` : nothing}
|
|
589
579
|
${actions.setVisibility
|
|
590
580
|
? isPrivate
|
|
591
|
-
? html`<
|
|
592
|
-
|
|
593
|
-
: html`<button class="icon" title=${t('make-private', 'Make private')} aria-label=${t('make-private', 'Make private')}
|
|
594
|
-
@click=${() => this.setVisibility(entry, 'private')}>${t('make-private', 'Make private')}</button>`
|
|
595
|
-
: nothing}
|
|
596
|
-
<button
|
|
597
|
-
class="icon"
|
|
598
|
-
title=${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}
|
|
599
|
-
aria-label=${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}
|
|
600
|
-
@click=${() => this.setHidden(entry, !entry.hidden)}
|
|
601
|
-
>
|
|
602
|
-
${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}
|
|
603
|
-
</button>
|
|
604
|
-
${actions.remove
|
|
605
|
-
? html`<button class="icon danger" title=${t('remove', 'Delete')} aria-label=${t('remove', 'Delete')}
|
|
606
|
-
@click=${() => this.askRemove(entry, name)}>${t('remove', 'Delete')}</button>`
|
|
581
|
+
? html`<ox-popup-menuitem label=${t('make-open', 'Make public')} @select=${() => this.askOpen(entry, name)}></ox-popup-menuitem>`
|
|
582
|
+
: html`<ox-popup-menuitem label=${t('make-private', 'Make private')} @select=${() => this.setVisibility(entry, 'private')}></ox-popup-menuitem>`
|
|
607
583
|
: nothing}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
584
|
+
<ox-popup-menuitem
|
|
585
|
+
label=${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}
|
|
586
|
+
@select=${() => this.setHidden(entry, !entry.hidden)}></ox-popup-menuitem>
|
|
587
|
+
${actions.remove ? html`<ox-popup-menuitem label=${t('remove', 'Delete')} @select=${() => this.askRemove(entry, name)}></ox-popup-menuitem>` : nothing}
|
|
588
|
+
`,
|
|
589
|
+
top: at.bottom,
|
|
590
|
+
right: window.innerWidth - at.right,
|
|
591
|
+
parent: document.body
|
|
592
|
+
})
|
|
611
593
|
}
|
|
612
594
|
|
|
613
595
|
/** Before what cannot be taken back - the platform's confirm, [Cancel][OK] (decided 2026-09-25, §7-3). */
|
|
@@ -31,6 +31,9 @@ export class AssistantSessionToolbar extends LitElement {
|
|
|
31
31
|
[role=tab]:hover { background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); color: var(--primary-color, #007f87); }
|
|
32
32
|
[aria-selected=true] { color:var(--primary-color, #007f87); border-bottom-color: currentColor; font-weight:600; }
|
|
33
33
|
.session-new { flex: none; padding: 0 7px; font-weight: 500; white-space: nowrap; }
|
|
34
|
+
/* The open conversation's name is one line - a long name is cut, and the whole of it is the tooltip. */
|
|
35
|
+
.list { flex: none; white-space: nowrap; }
|
|
36
|
+
.current { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
|
34
37
|
.session-new:hover, .history:hover { color: var(--primary-color, #007f87); background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); }
|
|
35
38
|
.history { height: 24px; margin: 1px 0 0 4px; padding: 0 5px; font-size: 11px; color: var(--tertiary-color, #88939a); }
|
|
36
39
|
:host([compact]) .controls { gap: 3px; min-height: 24px; }
|
|
@@ -47,20 +50,25 @@ export class AssistantSessionToolbar extends LitElement {
|
|
|
47
50
|
this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }))
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
private get currentName(): string {
|
|
54
|
+
return this.sessions.find(session => session.id === this.sessionId)?.name || i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
render() {
|
|
51
58
|
const label = i18next.t('ai-assistant.session.history', { defaultValue: 'Conversation history' })
|
|
52
59
|
return html`
|
|
53
60
|
<div class="controls">
|
|
54
|
-
${this.singleConversation ? html`<button @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.list', { defaultValue: 'Conversations' })}</button><span class="
|
|
61
|
+
${this.singleConversation ? html`<button class="list" @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.list', { defaultValue: 'Conversations' })}</button><span class="current" title=${this.currentName}>${this.currentName}</span>` : nothing}
|
|
55
62
|
${this.temporary || this.singleConversation ? nothing : html`<div class="tabs" role="tablist" aria-label=${label}>
|
|
56
63
|
${!this.sessionId ? html`<button role="tab" aria-selected="true">${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}
|
|
57
64
|
${assistantRecentSessions(this.sessions, this.sessionId, this.historyEnabled ? this.maxTabs : this.sessions.length).map((session, index) => html`<button role="tab" aria-selected=${session.id === this.sessionId ? 'true' : 'false'}
|
|
58
65
|
?disabled=${this.disabled || this.loading} title=${session.name || `${label} ${index + 1}`}
|
|
59
66
|
@click=${() => this.emit('assistant-session-select', { sessionId: session.id })}>${session.name || `${label} ${index + 1}`}</button>`)}
|
|
60
67
|
</div>`}
|
|
61
|
-
|
|
68
|
+
<!-- A conversation not yet started is already the new one - offering another names it twice. -->
|
|
69
|
+
${this.sessionId ? html`<button class="session-new" ?disabled=${this.disabled || this.loading || !!this.error}
|
|
62
70
|
title=${i18next.t('ai-assistant.session.reset-help', { defaultValue: 'Keep previous history and your work, and start a new conversation. Previous messages are excluded from subsequent requests.' })}
|
|
63
|
-
@click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button
|
|
71
|
+
@click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}
|
|
64
72
|
</div>
|
|
65
73
|
${this.historyEnabled && !this.singleConversation ? html`<button class="history" ?disabled=${this.loading || this.disabled}
|
|
66
74
|
@click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.all', { defaultValue: 'All History' })}</button>` : nothing}
|
|
@@ -46,6 +46,8 @@ export interface ChatMutationInputArgs {
|
|
|
46
46
|
* ones above. Omitted for a surface that declares no mode.
|
|
47
47
|
*/
|
|
48
48
|
mode?: { kind: string; target?: string }
|
|
49
|
+
/** The AI agent the person picked. Omitted, the domain default answers. */
|
|
50
|
+
agentId?: string
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -71,7 +73,8 @@ export function buildChatMutationInput(args: ChatMutationInputArgs): Record<stri
|
|
|
71
73
|
componentSchemas: args.componentSchemas,
|
|
72
74
|
selectedRefids: args.selectedRefids,
|
|
73
75
|
...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions }),
|
|
74
|
-
...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {})
|
|
76
|
+
...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {}),
|
|
77
|
+
...(args.agentId ? { agentId: args.agentId } : {})
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
|
|
@@ -96,6 +99,8 @@ export interface AssistantChatInputArgs {
|
|
|
96
99
|
hostContext?: any
|
|
97
100
|
/** Files stored with the conversation by attachToConversation, sent with this turn. */
|
|
98
101
|
attachmentIds?: string[]
|
|
102
|
+
/** The AI agent the person picked. Omitted, the domain default answers. */
|
|
103
|
+
agentId?: string
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
/**
|
|
@@ -119,6 +124,7 @@ export function buildAssistantChatInput(args: AssistantChatInputArgs): Record<st
|
|
|
119
124
|
mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) },
|
|
120
125
|
...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),
|
|
121
126
|
...(args.hostContext ? { hostContext: args.hostContext } : {}),
|
|
122
|
-
...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {})
|
|
127
|
+
...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {}),
|
|
128
|
+
...(args.agentId ? { agentId: args.agentId } : {})
|
|
123
129
|
}
|
|
124
130
|
}
|