@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.
Files changed (121) hide show
  1. package/client/bootstrap.ts +27 -0
  2. package/client/components/assistant-chat.ts +229 -178
  3. package/client/components/assistant-dock.ts +53 -48
  4. package/client/components/assistant-session-manager.ts +82 -100
  5. package/client/components/assistant-session-toolbar.ts +11 -3
  6. package/client/components/chat-input-builder.ts +8 -2
  7. package/client/settings/agent-settings-model.ts +164 -0
  8. package/client/settings/ai-agent-setting-let.ts +684 -0
  9. package/client/settings/ai-usage-setting-let.ts +252 -0
  10. package/client/settings/usage-model.ts +46 -0
  11. package/client/setup-assistant-dock.ts +2 -3
  12. package/client/utils/agent-choice.ts +56 -0
  13. package/client/utils/assistant-navigation.test.ts +14 -8
  14. package/client/utils/assistant-navigation.ts +8 -3
  15. package/client/utils/composer-frame.ts +28 -2
  16. package/client/utils/menu-room.ts +33 -0
  17. package/dist-client/bootstrap.d.ts +8 -0
  18. package/dist-client/bootstrap.js +26 -0
  19. package/dist-client/bootstrap.js.map +1 -0
  20. package/dist-client/components/assistant-chat.d.ts +32 -4
  21. package/dist-client/components/assistant-chat.js +234 -175
  22. package/dist-client/components/assistant-chat.js.map +1 -1
  23. package/dist-client/components/assistant-dock.d.ts +6 -8
  24. package/dist-client/components/assistant-dock.js +52 -60
  25. package/dist-client/components/assistant-dock.js.map +1 -1
  26. package/dist-client/components/assistant-session-manager.d.ts +8 -7
  27. package/dist-client/components/assistant-session-manager.js +84 -124
  28. package/dist-client/components/assistant-session-manager.js.map +1 -1
  29. package/dist-client/components/assistant-session-toolbar.d.ts +1 -0
  30. package/dist-client/components/assistant-session-toolbar.js +10 -3
  31. package/dist-client/components/assistant-session-toolbar.js.map +1 -1
  32. package/dist-client/components/chat-input-builder.d.ts +4 -0
  33. package/dist-client/components/chat-input-builder.js +4 -2
  34. package/dist-client/components/chat-input-builder.js.map +1 -1
  35. package/dist-client/settings/agent-settings-model.d.ts +93 -0
  36. package/dist-client/settings/agent-settings-model.js +95 -0
  37. package/dist-client/settings/agent-settings-model.js.map +1 -0
  38. package/dist-client/settings/ai-agent-setting-let.d.ts +51 -0
  39. package/dist-client/settings/ai-agent-setting-let.js +672 -0
  40. package/dist-client/settings/ai-agent-setting-let.js.map +1 -0
  41. package/dist-client/settings/ai-usage-setting-let.d.ts +26 -0
  42. package/dist-client/settings/ai-usage-setting-let.js +276 -0
  43. package/dist-client/settings/ai-usage-setting-let.js.map +1 -0
  44. package/dist-client/settings/usage-model.d.ts +32 -0
  45. package/dist-client/settings/usage-model.js +25 -0
  46. package/dist-client/settings/usage-model.js.map +1 -0
  47. package/dist-client/setup-assistant-dock.d.ts +0 -1
  48. package/dist-client/setup-assistant-dock.js +0 -1
  49. package/dist-client/setup-assistant-dock.js.map +1 -1
  50. package/dist-client/tsconfig.tsbuildinfo +1 -1
  51. package/dist-client/utils/agent-choice.d.ts +24 -0
  52. package/dist-client/utils/agent-choice.js +38 -0
  53. package/dist-client/utils/agent-choice.js.map +1 -0
  54. package/dist-client/utils/assistant-navigation.d.ts +8 -3
  55. package/dist-client/utils/assistant-navigation.js +7 -2
  56. package/dist-client/utils/assistant-navigation.js.map +1 -1
  57. package/dist-client/utils/assistant-navigation.test.js +14 -8
  58. package/dist-client/utils/assistant-navigation.test.js.map +1 -1
  59. package/dist-client/utils/composer-frame.d.ts +23 -2
  60. package/dist-client/utils/composer-frame.js +20 -2
  61. package/dist-client/utils/composer-frame.js.map +1 -1
  62. package/dist-client/utils/menu-room.d.ts +13 -0
  63. package/dist-client/utils/menu-room.js +26 -0
  64. package/dist-client/utils/menu-room.js.map +1 -0
  65. package/dist-server/service/ai-agent-resolver.d.ts +82 -0
  66. package/dist-server/service/ai-agent-resolver.js +417 -0
  67. package/dist-server/service/ai-agent-resolver.js.map +1 -0
  68. package/dist-server/service/assistant-capability-resolver.d.ts +1 -1
  69. package/dist-server/service/assistant-capability-resolver.js +7 -7
  70. package/dist-server/service/assistant-capability-resolver.js.map +1 -1
  71. package/dist-server/service/assistant-chat-resolver.d.ts +2 -0
  72. package/dist-server/service/assistant-chat-resolver.js +32 -15
  73. package/dist-server/service/assistant-chat-resolver.js.map +1 -1
  74. package/dist-server/service/assistant-mode/resolve.d.ts +0 -1
  75. package/dist-server/service/assistant-mode/resolve.js +0 -1
  76. package/dist-server/service/assistant-mode/resolve.js.map +1 -1
  77. package/dist-server/service/assistant-mode/types.d.ts +1 -2
  78. package/dist-server/service/assistant-mode/types.js.map +1 -1
  79. package/dist-server/service/chat-session/chat-session.d.ts +2 -0
  80. package/dist-server/service/chat-session/chat-session.js +5 -0
  81. package/dist-server/service/chat-session/chat-session.js.map +1 -1
  82. package/dist-server/service/index.d.ts +2 -1
  83. package/dist-server/service/index.js +3 -0
  84. package/dist-server/service/index.js.map +1 -1
  85. package/dist-server/service/testing/mode-conformance.js +0 -1
  86. package/dist-server/service/testing/mode-conformance.js.map +1 -1
  87. package/dist-server/tsconfig.tsbuildinfo +1 -1
  88. package/package.json +9 -7
  89. package/server/service/ai-agent-resolver.ts +330 -0
  90. package/server/service/assistant-capability-resolver.ts +7 -7
  91. package/server/service/assistant-chat-resolver.ts +32 -24
  92. package/server/service/assistant-mode/resolve.test.ts +0 -2
  93. package/server/service/assistant-mode/resolve.ts +0 -2
  94. package/server/service/assistant-mode/types.ts +1 -2
  95. package/server/service/chat-session/chat-session.ts +5 -0
  96. package/server/service/index.ts +3 -0
  97. package/server/service/testing/mode-conformance.ts +0 -1
  98. package/server/tool-registry-boot-report.test.ts +2 -0
  99. package/test/ai-agent-settings.test.ts +123 -0
  100. package/test/assistant-dock.test.ts +3 -2
  101. package/test/assistant-session-manager.test.ts +10 -4
  102. package/test/assistant-session-toolbar.test.ts +8 -0
  103. package/test/composer-frame.test.ts +40 -2
  104. package/test/menu-room.test.ts +33 -0
  105. package/test/open-doors.test.ts +3 -1
  106. package/test/session-discovery.test.ts +7 -6
  107. package/things-factory.config.js +2 -0
  108. package/translations/en.json +78 -12
  109. package/translations/es.json +78 -12
  110. package/translations/ja.json +78 -12
  111. package/translations/ko.json +78 -12
  112. package/translations/ms.json +78 -12
  113. package/translations/zh.json +78 -12
  114. package/client/components/chat-triggers.test.ts +0 -72
  115. package/client/components/chat-triggers.ts +0 -62
  116. package/dist-client/components/chat-triggers.d.ts +0 -16
  117. package/dist-client/components/chat-triggers.js +0 -42
  118. package/dist-client/components/chat-triggers.js.map +0 -1
  119. package/dist-client/components/chat-triggers.test.d.ts +0 -1
  120. package/dist-client/components/chat-triggers.test.js +0 -61
  121. package/dist-client/components/chat-triggers.test.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-session-manager.js","sourceRoot":"","sources":["../../client/components/assistant-session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing, type PropertyValues, type TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup'\n\nimport {\n assistantSessionAdmin,\n sessionActions,\n type AssistantSessionAdmin,\n type AssistantSessionEntry,\n type AssistantSessionFilters\n} from '../utils/assistant-session-admin.js'\n\nconst PAGE = 30\n\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n i18next.t(`ai-assistant.session.${key}`, { defaultValue, ...options })\n\n/**\n * The conversations on one anchor, managed - `<ox-assistant-session-manager>`.\n *\n * Search, a filter (all / mine / including hidden), paging, and on each row: open, rename, hide or\n * unhide, private ⇄ public, delete. Taken from operato-twin's dock, where it was written first, so every\n * dock gets it by placing this element (typically behind the toolbar's \"All History\").\n *\n * ── What is the host's ──────────────────────────────────────────────────────────\n * Opening and starting a conversation belong to the dock's session controller, so this element asks\n * with the toolbar's own events and does not open anything itself:\n * assistant-session-select { sessionId } open that conversation\n * assistant-session-reset start a new one\n * assistant-session-changed { sessionId, change: 'renamed' | 'hidden' | 'unhidden' | 'removed' | 'visibility' }\n * a row changed - the host refreshes its tabs; if the open\n * conversation was removed or hidden, it moves off it\n *\n * ── What is the server's ────────────────────────────────────────────────────────\n * Who may do what (`sessionActions`): rename any participant, hide anyone for their own list, delete\n * the creator, visibility the owner. The buttons follow those hints; the doors decide again.\n *\n * ── Asking before what cannot be undone ─────────────────────────────────────────\n * Delete, and opening a private conversation to the domain, ask on the row itself - [Cancel][OK].\n * Making it private, hiding and renaming do not: each can be undone from the same row.\n */\n@customElement('ox-assistant-session-manager')\nexport class AssistantSessionManager extends LitElement {\n static styles = css`\n .scope-label { padding: 12px 12px 0; font-size: 14px; font-weight: 600; }\n .filter-toggle, .reset { margin: 6px 10px; padding: 7px; font: inherit; background: transparent; color: inherit; border: 1px solid var(--md-sys-color-outline-variant); border-radius: 6px; cursor: pointer; }\n .filter-toggle { align-self: flex-end; }\n .filters[hidden] { display: none; }\n .open-row { display: block; flex: 1; min-width: 0; text-align: start; border: 0; background: transparent; color: inherit; font: inherit; padding: 4px; cursor: pointer; }\n .meta { display: block; margin-top: 5px; font-size: 12px; color: var(--md-sys-color-on-surface-variant); }\n /* Open and private must part at a glance - an open row is read by everyone in the domain (2026-09-30). */\n .audience { display: inline-flex; align-items: center; gap: 3px; vertical-align: middle; }\n .audience md-icon { --md-icon-size: 14px; }\n /* The dock's warning colour, fixed across applications - see assistant-dock.ts. */\n :host { --assistant-open-edge: #c47f00; --assistant-open-surface: #f2b01e; --assistant-open-ink: #2e2100; }\n .audience.open { padding: 1px 6px 1px 4px; border-radius: 6px; background: var(--assistant-open-surface); color: var(--assistant-open-ink); font-weight: 600; }\n .row.open-audience { box-shadow: inset 4px 0 0 var(--assistant-open-edge); }\n .actions summary { cursor: pointer; padding: 8px; font-size: 18px; list-style: none; }\n .actions[open] .acts { display: flex; flex-direction: column; position: absolute; right: 10px; top: 40px; z-index: 2; min-width: 180px; padding: 6px; background: var(--md-sys-color-surface-container-lowest); border: 1px solid var(--md-sys-color-outline-variant); border-radius: 8px; box-shadow: 0 4px 16px #0002; }\n .actions button.icon { min-height: 40px; padding: 8px 12px; font: inherit; text-align: start; }\n .actions button.danger { color: var(--md-sys-color-error, #b42318); }\n button:focus-visible, summary:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 2px; }\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n font-size: 14px;\n color: var(--md-sys-color-on-surface);\n }\n\n .bar {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n .bar input,\n .bar select {\n min-width: 0;\n height: 28px;\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n padding: 0 8px;\n }\n\n .bar input {\n flex: 1;\n }\n\n .new {\n flex: none;\n border: 0;\n border-radius: 6px;\n padding: 4px 8px;\n background: transparent;\n color: var(--md-sys-color-primary);\n font: inherit;\n font-weight: 650;\n cursor: pointer;\n }\n\n .filters {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px 10px;\n padding: 6px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n font-size: 11.5px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .filters select {\n height: 26px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n }\n\n .filters label {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n cursor: pointer;\n }\n\n .kind {\n color: var(--md-sys-color-primary);\n }\n\n .list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .row {\n position: relative;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 9px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n cursor: pointer;\n }\n\n .row:hover,\n .row.current {\n background: var(--md-sys-color-surface-container);\n }\n\n .row.hidden {\n opacity: 0.6;\n }\n\n .dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n }\n\n .row.unread .dot {\n background: var(--md-sys-color-primary);\n }\n\n .row md-icon {\n --md-icon-size: 16px;\n flex: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .body {\n flex: 1;\n min-width: 0;\n }\n\n .title,\n .prev {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .row.unread .title {\n font-weight: 700;\n }\n\n .prev {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 11px;\n }\n\n /* Row actions show on hover and on keyboard focus - the list's body is the names. */\n .acts {\n display: none;\n gap: 2px;\n }\n\n button.icon {\n display: inline-flex;\n padding: 3px;\n border: 0;\n border-radius: 6px;\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n\n button.icon:hover {\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface);\n }\n\n button.icon.danger:hover {\n color: var(--md-sys-color-error);\n }\n\n .rename {\n flex: 1;\n min-width: 0;\n height: 26px;\n border: 1px solid var(--md-sys-color-primary);\n border-radius: 6px;\n padding: 0 6px;\n font: inherit;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n }\n\n .ask {\n display: grid;\n gap: 6px;\n padding: 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background: var(--md-sys-color-surface-container);\n }\n\n .ask .buttons {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n }\n\n .ask button {\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n padding: 4px 10px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n .ask button.ok {\n border-color: var(--md-sys-color-primary);\n background: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n }\n\n .ask button.ok.danger {\n border-color: var(--md-sys-color-error);\n background: var(--md-sys-color-error);\n color: var(--md-sys-color-on-error);\n }\n\n .note,\n .empty {\n padding: 14px 12px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [role='alert'] {\n color: var(--md-sys-color-error);\n }\n\n .more,\n .hiddenfilter {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n padding: 8px;\n border: 0;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n font: inherit;\n cursor: pointer;\n }\n\n .hiddenfilter md-icon {\n --md-icon-size: 16px;\n }\n `\n\n /** The anchor whose conversations are listed - the same one the dock's session controller opened. */\n @property({ type: String }) anchorType = ''\n @property({ type: String }) anchorId = ''\n /** The conversation the dock has open - marked in the list. */\n @property({ type: String }) sessionId = ''\n @property({ type: String }) scopeLabel = ''\n /** The doors. Replaceable for tests; a host normally leaves it. */\n @property({ attribute: false }) admin: AssistantSessionAdmin = assistantSessionAdmin\n /** The conversation kind (mode) open in the dock - the list's default scope (§2-6). */\n @property({ type: String }) kind = ''\n /** kind → the words people know it by (the modes' labels), for each row. */\n @property({ attribute: false }) kindLabels: Record<string, string> = {}\n\n @state() private entries: AssistantSessionEntry[] = []\n @state() private total = 0\n @state() private hiddenCount = 0\n @state() private search = ''\n @state() private allKinds = false\n @state() private whose: NonNullable<AssistantSessionFilters['whose']> = 'all'\n @state() private visibility: NonNullable<AssistantSessionFilters['visibility']> = 'all'\n @state() private includeHidden = false\n @state() private unreadOnly = false\n @state() private pageSize = PAGE\n @state() private loading = false\n @state() private error = ''\n @state() private renamingId = ''\n @state() private filtersOpen = false\n\n private get hasFilters() {\n return !!this.search.trim() || this.allKinds || this.whose !== 'all' || this.visibility !== 'all' || this.includeHidden || this.unreadOnly\n }\n\n private resetFilters() {\n clearTimeout(this.searchTimer)\n this.refilter(() => {\n this.search = ''\n this.allKinds = false\n this.whose = 'all'\n this.visibility = 'all'\n this.includeHidden = this.unreadOnly = false\n })\n }\n\n private generation = 0\n private searchTimer?: ReturnType<typeof setTimeout>\n\n /** Read the list again - the host calls this after it opened or started a conversation. */\n async reload(): Promise<void> {\n const generation = ++this.generation\n if (!this.anchorType || !this.anchorId) {\n this.entries = []\n this.total = this.hiddenCount = 0\n return\n }\n\n this.loading = true\n this.error = ''\n try {\n /*\n * Paging reads again from the start rather than appending - live activity reorders the list, and\n * appending a later page to an earlier order duplicates and drops rows.\n */\n const page = await this.admin.inbox({\n anchorType: this.anchorType,\n anchorId: this.anchorId,\n search: this.search,\n filters: {\n kind: this.allKinds ? '' : this.kind,\n whose: this.whose,\n visibility: this.visibility,\n includeHidden: this.includeHidden,\n unreadOnly: this.unreadOnly\n },\n limit: this.pageSize,\n offset: 0\n })\n if (generation !== this.generation) return\n this.entries = page.entries\n this.total = page.total\n this.hiddenCount = page.hiddenCount\n } catch (error) {\n if (generation !== this.generation) return\n this.entries = []\n this.total = this.hiddenCount = 0\n this.error = (error as Error).message || t('failed', 'could not load the conversation list.')\n } finally {\n if (generation === this.generation) this.loading = false\n }\n }\n\n protected updated(changed: PropertyValues) {\n if (changed.has('anchorType') || changed.has('anchorId') || changed.has('admin') || changed.has('kind')) void this.reload()\n\n /*\n * The rename box takes the keys the moment it opens, with the name selected. `autofocus` works once\n * per document, so the second rename opened a box that ignored the typing (2026-09-24, operato-ecs).\n */\n if (changed.has('renamingId') && this.renamingId) {\n const input = this.renderRoot.querySelector('input.rename') as HTMLInputElement | null\n input?.focus()\n input?.select()\n }\n }\n\n disconnectedCallback() {\n this.generation++\n clearTimeout(this.searchTimer)\n super.disconnectedCallback()\n }\n\n private emit(name: string, detail?: unknown) {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }))\n }\n\n /* ── what the person does ────────────────────────────────────────────────────────── */\n\n private onSearch(value: string) {\n this.search = value\n clearTimeout(this.searchTimer)\n /* Not on every keystroke. A new search starts from the first page. */\n this.searchTimer = setTimeout(() => {\n this.pageSize = PAGE\n void this.reload()\n }, 200)\n }\n\n /** A filter changed - the list starts from its first page again. */\n private refilter(change: () => void) {\n change()\n this.pageSize = PAGE\n void this.reload()\n }\n\n private loadMore() {\n this.pageSize += PAGE\n void this.reload()\n }\n\n /** Runs one door, then reads the list again - on success and on failure alike, so the row shows the truth. */\n private async act(entry: AssistantSessionEntry, change: string, run: () => Promise<boolean>, refused: string) {\n this.renamingId = ''\n try {\n if (!(await run())) this.error = refused\n else this.emit('assistant-session-changed', { sessionId: entry.id, change })\n } catch (error) {\n /* Never pass a failure silently - a person must not believe a conversation was deleted when it was not. */\n this.error = (error as Error).message\n }\n const error = this.error\n await this.reload()\n if (error && !this.error) this.error = error\n }\n\n private rename(entry: AssistantSessionEntry, name: string) {\n const trimmed = name.trim()\n this.renamingId = ''\n if (!trimmed || trimmed === entry.name) return\n void this.act(entry, 'renamed', () => this.admin.rename(entry.id, trimmed), t('rename-refused', 'this conversation could not be renamed.'))\n }\n\n private setHidden(entry: AssistantSessionEntry, hidden: boolean) {\n void this.act(entry, hidden ? 'hidden' : 'unhidden', () => this.admin.hide(entry.id, hidden), t('hide-refused', 'this conversation could not be hidden.'))\n }\n\n private setVisibility(entry: AssistantSessionEntry, visibility: 'open' | 'private') {\n void this.act(\n entry,\n 'visibility',\n () => this.admin.setVisibility(entry.id, visibility),\n t('visibility-refused', 'only the owner of this conversation can change who reads it.')\n )\n }\n\n private _removeSession(entry: AssistantSessionEntry) {\n void this.act(entry, 'removed', () => this.admin.remove(entry.id), t('remove-refused', 'this conversation could not be deleted.'))\n }\n\n /* ── drawing ─────────────────────────────────────────────────────────────────────── */\n\n render(): TemplateResult {\n const shown = this.entries.length\n\n return html`\n <div class=\"scope-label\">${this.scopeLabel}</div>\n <div class=\"bar\">\n <input\n .value=${this.search}\n placeholder=${t('search', 'Search conversations')}\n aria-label=${t('search', 'Search conversations')}\n @input=${(e: Event) => this.onSearch((e.target as HTMLInputElement).value)}\n @keydown=${(e: KeyboardEvent) => { if (e.key === 'Escape' && this.search) { e.stopPropagation(); this.onSearch('') } }}\n />\n <button class=\"new\" @click=${() => this.emit('assistant-session-reset')}>+ ${t('new', 'New conversation')}</button>\n </div>\n <button class=\"filter-toggle\" aria-expanded=${this.filtersOpen} @click=${() => (this.filtersOpen = !this.filtersOpen)}>${t('filters', 'Filters')}${this.hasFilters ? ' •' : ''}</button>\n <div class=\"filters\" ?hidden=${!this.filtersOpen}>\n ${this.kind\n ? html`<select aria-label=${t('filter-kind', 'Which kind')} .value=${this.allKinds ? 'all' : 'this'}\n @change=${(e: Event) => this.refilter(() => (this.allKinds = (e.target as HTMLSelectElement).value === 'all'))}>\n <option value=\"this\">${this.kindLabels[this.kind] || t('filter-this-kind', 'This conversation')}</option>\n <option value=\"all\">${t('filter-all-kinds', 'All kinds here')}</option>\n </select>`\n : nothing}\n <select aria-label=${t('filter-whose', 'Whose')} .value=${this.whose}\n @change=${(e: Event) => this.refilter(() => (this.whose = (e.target as HTMLSelectElement).value as any))}>\n <option value=\"all\">${t('filter-all', 'All')}</option>\n <option value=\"mine\">${t('filter-mine', 'Started by me')}</option>\n <option value=\"participant\">${t('filter-participant', \"I'm in\")}</option>\n </select>\n <select aria-label=${t('filter-visibility', 'Visibility')} .value=${this.visibility}\n @change=${(e: Event) => this.refilter(() => (this.visibility = (e.target as HTMLSelectElement).value as any))}>\n <option value=\"all\">${t('filter-any-visibility', 'Public and private')}</option>\n <option value=\"open\">${t('filter-open', 'Public')}</option>\n <option value=\"private\">${t('filter-private', 'Private')}</option>\n </select>\n <label><input type=\"checkbox\" .checked=${this.unreadOnly}\n @change=${(e: Event) => this.refilter(() => (this.unreadOnly = (e.target as HTMLInputElement).checked))} />${t('filter-unread', 'Unread')}</label>\n <label><input type=\"checkbox\" .checked=${this.includeHidden}\n @change=${(e: Event) => this.refilter(() => (this.includeHidden = (e.target as HTMLInputElement).checked))} />${t('filter-hidden', 'Including hidden')}</label>\n </div>\n ${this.hasFilters ? html`<button class=\"reset\" @click=${() => this.resetFilters()}>${t('reset-filters', 'Clear search and filters')}</button>` : nothing}\n <div class=\"list\">\n ${this.error ? html`<div class=\"note\" role=\"alert\">${this.error}</div>` : nothing}\n ${this.loading ? html`<div class=\"note\" role=\"status\">${t('loading', 'Loading conversations…')}</div>` : nothing}\n ${shown === 0 && !this.loading && !this.error\n ? html`<div class=\"empty\" role=\"status\">${this.hasFilters ? t('filtered-empty', 'No conversations match these filters here.') : t('scope-empty', 'No conversations for this subject yet.')}</div>`\n : this.entries.map(entry => this.renderRow(entry))}\n ${shown < this.total\n ? html`<button class=\"more\" @click=${() => this.loadMore()}>${t('load-more', 'Show more ({rest})', { rest: this.total - shown })}</button>`\n : nothing}\n </div>\n <!-- A hidden conversation must not be forgotten: say how many, and one press shows them. -->\n ${this.hiddenCount > 0 && !this.includeHidden\n ? html`<button class=\"hiddenfilter\" @click=${() => this.refilter(() => (this.includeHidden = true))}>\n <md-icon>visibility_off</md-icon>${t('hidden-count', '{count} hidden', { count: this.hiddenCount })}\n </button>`\n : nothing}\n `\n }\n\n private renderRow(entry: AssistantSessionEntry): TemplateResult {\n const name = entry.name || t('untitled', 'Untitled conversation')\n const actions = sessionActions(entry)\n const isPrivate = entry.visibility === 'private'\n\n if (this.renamingId === entry.id) {\n return html`<div class=\"row\">\n <span class=\"dot\"></span>\n <input\n class=\"rename\"\n .value=${entry.name || ''}\n aria-label=${t('rename', 'Rename')}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter') this.rename(entry, (e.target as HTMLInputElement).value)\n if (e.key === 'Escape') { e.stopPropagation(); this.renamingId = '' }\n }}\n @blur=${(e: Event) => this.rename(entry, (e.target as HTMLInputElement).value)}\n />\n </div>`\n }\n\n return html`<div\n class=\"row ${entry.unread ? 'unread' : ''} ${entry.hidden ? 'hidden' : ''} ${entry.id === this.sessionId ? 'current' : ''} ${isPrivate ? '' : 'open-audience'}\"\n >\n <span class=\"dot\"></span>\n <button class=\"open-row\" aria-current=${entry.id === this.sessionId ? 'true' : 'false'} @click=${() => this.emit('assistant-session-select', { sessionId: entry.id, kind: entry.kind })}>\n <div class=\"title\">${name}</div>\n <div class=\"prev\">${this.allKinds && entry.kind ? html`<span class=\"kind\">${this.kindLabels[entry.kind] || entry.kind}</span> · ` : nothing}${entry.lastMessagePreview || ''}</div>\n <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>\n </button>\n <details class=\"actions\" @keydown=${(e: KeyboardEvent) => { if (e.key === 'Escape') { e.stopPropagation(); const details = e.currentTarget as HTMLDetailsElement; details.open = false; details.querySelector('summary')?.focus() } }}>\n <summary aria-label=${t('manage', 'Manage conversation')}>⋯</summary>\n <span class=\"acts\" @click=${(e: Event) => e.stopPropagation()}>\n ${actions.rename\n ? html`<button class=\"icon\" title=${t('rename', 'Rename')} aria-label=${t('rename', 'Rename')} @click=${() => (this.renamingId = entry.id)}>\n ${t('rename', 'Rename')}\n </button>`\n : nothing}\n ${actions.setVisibility\n ? isPrivate\n ? html`<button class=\"icon\" title=${t('make-open', 'Make public')} aria-label=${t('make-open', 'Make public')}\n @click=${() => this.askOpen(entry, name)}>${t('make-open', 'Make public')}</button>`\n : html`<button class=\"icon\" title=${t('make-private', 'Make private')} aria-label=${t('make-private', 'Make private')}\n @click=${() => this.setVisibility(entry, 'private')}>${t('make-private', 'Make private')}</button>`\n : nothing}\n <button\n class=\"icon\"\n title=${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}\n aria-label=${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}\n @click=${() => this.setHidden(entry, !entry.hidden)}\n >\n ${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}\n </button>\n ${actions.remove\n ? html`<button class=\"icon danger\" title=${t('remove', 'Delete')} aria-label=${t('remove', 'Delete')}\n @click=${() => this.askRemove(entry, name)}>${t('remove', 'Delete')}</button>`\n : nothing}\n </span>\n </details>\n </div>`\n }\n\n /** Before what cannot be taken back - the platform's confirm, [Cancel][OK] (decided 2026-09-25, §7-3). */\n private async ask(title: string, text: string, confirmText: string): Promise<boolean> {\n return !!(await OxPrompt.open({\n type: 'warning',\n title,\n text,\n confirmButton: { text: confirmText },\n cancelButton: { text: t('cancel', 'Cancel') }\n }))\n }\n\n private async askRemove(entry: AssistantSessionEntry, name: string) {\n const ok = await this.ask(\n t('remove', 'Delete'),\n t('confirm-remove', 'Delete \"{name}\"? It disappears for every participant and cannot be brought back.', { name }),\n t('remove', 'Delete')\n )\n if (ok) this._removeSession(entry)\n }\n\n private async askOpen(entry: AssistantSessionEntry, name: string) {\n const ok = await this.ask(\n t('make-open', 'Make public'),\n t('confirm-open', 'Make \"{name}\" public? Everyone in this domain will be able to read what is already written.', { name }),\n t('make-open', 'Make public')\n )\n if (ok) this.setVisibility(entry, 'open')\n }\n}\n"]}
1
+ {"version":3,"file":"assistant-session-manager.js","sourceRoot":"","sources":["../../client/components/assistant-session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing, type PropertyValues, type TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup'\nimport '@operato/popup/ox-popup-menu.js'\nimport { OxPopupMenu } from '@operato/popup/ox-popup-menu.js'\n\nimport {\n assistantSessionAdmin,\n sessionActions,\n type AssistantSessionAdmin,\n type AssistantSessionEntry,\n type AssistantSessionFilters\n} from '../utils/assistant-session-admin.js'\n\nconst PAGE = 30\n\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n i18next.t(`ai-assistant.session.${key}`, { defaultValue, ...options })\n\n/**\n * The conversations on one anchor, managed - `<ox-assistant-session-manager>`.\n *\n * Search, a filter (all / mine / including hidden), paging, and on each row: open, rename, hide or\n * unhide, private ⇄ public, delete. Taken from operato-twin's dock, where it was written first, so every\n * dock gets it by placing this element (typically behind the toolbar's \"All History\").\n *\n * ── What is the host's ──────────────────────────────────────────────────────────\n * Opening and starting a conversation belong to the dock's session controller, so this element asks\n * with the toolbar's own events and does not open anything itself:\n * assistant-session-select { sessionId } open that conversation\n * assistant-session-reset start a new one\n * assistant-session-changed { sessionId, change: 'renamed' | 'hidden' | 'unhidden' | 'removed' | 'visibility' }\n * a row changed - the host refreshes its tabs; if the open\n * conversation was removed or hidden, it moves off it\n *\n * ── What is the server's ────────────────────────────────────────────────────────\n * Who may do what (`sessionActions`): rename any participant, hide anyone for their own list, delete\n * the creator, visibility the owner. The buttons follow those hints; the doors decide again.\n *\n * ── Asking before what cannot be undone ─────────────────────────────────────────\n * Delete, and opening a private conversation to the domain, ask on the row itself - [Cancel][OK].\n * Making it private, hiding and renaming do not: each can be undone from the same row.\n */\n@customElement('ox-assistant-session-manager')\nexport class AssistantSessionManager extends LitElement {\n static styles = css`\n /* One scale for the list: name 13, preview 12, meta 11 - the rest of the dock's sizes. */\n .open-row { display: block; flex: 1; min-width: 0; text-align: start; border: 0; background: transparent; color: inherit; font: inherit; padding: 0; cursor: pointer; }\n .meta { display: block; margin-top: 2px; font-size: 11px; color: var(--md-sys-color-on-surface-variant); }\n /* Open and private must part at a glance - an open row is read by everyone in the domain (2026-09-30). */\n .audience { display: inline-flex; align-items: center; gap: 3px; vertical-align: middle; }\n .audience md-icon { --md-icon-size: 14px; }\n /* The dock's warning colour, fixed across applications - see assistant-dock.ts. */\n :host { --assistant-open-edge: #c47f00; --assistant-open-surface: #f2b01e; --assistant-open-ink: #2e2100; }\n .audience.open { padding: 0 5px 0 3px; border-radius: 4px; background: var(--assistant-open-surface); color: var(--assistant-open-ink); font-weight: 600; }\n .audience md-icon { --md-icon-size: 12px; }\n .row.open-audience { box-shadow: inset 4px 0 0 var(--assistant-open-edge); }\n .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; }\n .more-menu:hover { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }\n button:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 2px; }\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n font-size: 13px;\n color: var(--md-sys-color-on-surface);\n }\n\n .bar {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px 6px;\n }\n\n .bar input,\n .bar select {\n min-width: 0;\n height: 26px;\n font-size: 12px;\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n padding: 0 8px;\n }\n\n .bar input {\n flex: 1;\n }\n\n .new {\n flex: none;\n border: 0;\n border-radius: 6px;\n padding: 4px 8px;\n background: transparent;\n color: var(--md-sys-color-primary);\n font: inherit;\n font-size: 12px;\n font-weight: 600;\n white-space: nowrap;\n cursor: pointer;\n }\n\n /* Always open, one quiet line under the search - the same small size as the search. */\n .filters {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n padding: 0 10px 8px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n font-size: 11px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .filters select {\n height: 22px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n padding: 0 4px;\n }\n\n .filters label {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n cursor: pointer;\n }\n\n .filters input[type='checkbox'] {\n margin: 0;\n }\n\n .filters .clear {\n margin-left: auto;\n border: 0;\n background: none;\n padding: 0;\n color: var(--md-sys-color-primary);\n font: inherit;\n cursor: pointer;\n }\n\n .list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .row {\n position: relative;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n cursor: pointer;\n }\n\n .row:hover,\n .row.current {\n background: var(--md-sys-color-surface-container);\n }\n\n .row.hidden {\n opacity: 0.6;\n }\n\n .dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n }\n\n .row.unread .dot {\n background: var(--md-sys-color-primary);\n }\n\n .row md-icon {\n --md-icon-size: 16px;\n flex: none;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .body {\n flex: 1;\n min-width: 0;\n }\n\n .title,\n .prev {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .row.unread .title {\n font-weight: 700;\n }\n\n .prev {\n margin-top: 1px;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .rename {\n flex: 1;\n min-width: 0;\n height: 26px;\n border: 1px solid var(--md-sys-color-primary);\n border-radius: 6px;\n padding: 0 6px;\n font: inherit;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n }\n\n .ask {\n display: grid;\n gap: 6px;\n padding: 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background: var(--md-sys-color-surface-container);\n }\n\n .ask .buttons {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n }\n\n .ask button {\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n padding: 4px 10px;\n background: var(--md-sys-color-surface-container-lowest);\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n .ask button.ok {\n border-color: var(--md-sys-color-primary);\n background: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n }\n\n .ask button.ok.danger {\n border-color: var(--md-sys-color-error);\n background: var(--md-sys-color-error);\n color: var(--md-sys-color-on-error);\n }\n\n .note,\n .empty {\n padding: 14px 12px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [role='alert'] {\n color: var(--md-sys-color-error);\n }\n\n .more,\n .hiddenfilter {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n padding: 8px;\n border: 0;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n background: none;\n color: var(--md-sys-color-on-surface-variant);\n font: inherit;\n cursor: pointer;\n }\n\n .hiddenfilter md-icon {\n --md-icon-size: 16px;\n }\n `\n\n /** The anchor whose conversations are listed - the same one the dock's session controller opened. */\n @property({ type: String }) anchorType = ''\n @property({ type: String }) anchorId = ''\n /** The conversation the dock has open - marked in the list. */\n @property({ type: String }) sessionId = ''\n /** The doors. Replaceable for tests; a host normally leaves it. */\n @property({ attribute: false }) admin: AssistantSessionAdmin = assistantSessionAdmin\n /** The conversation kind (mode) open in the dock - the list's default scope (§2-6). */\n @property({ type: String }) kind = ''\n\n @state() private entries: AssistantSessionEntry[] = []\n @state() private total = 0\n @state() private hiddenCount = 0\n @state() private search = ''\n @state() private whose: NonNullable<AssistantSessionFilters['whose']> = 'all'\n @state() private visibility: NonNullable<AssistantSessionFilters['visibility']> = 'all'\n @state() private includeHidden = false\n @state() private unreadOnly = false\n @state() private pageSize = PAGE\n @state() private loading = false\n @state() private error = ''\n @state() private renamingId = ''\n\n /*\n * The search and the filters stand open together, always (2026-10-12) - a filter behind a \"Filters\" button is one\n * more thing to find before finding the conversation. The list is this screen's conversations, so there is no kind\n * to choose.\n */\n private get filtering() {\n return !!this.search.trim() || this.whose !== 'all' || this.visibility !== 'all' || this.includeHidden || this.unreadOnly\n }\n\n private clearFilters() {\n clearTimeout(this.searchTimer)\n this.refilter(() => {\n this.search = ''\n this.whose = 'all'\n this.visibility = 'all'\n this.includeHidden = this.unreadOnly = false\n })\n }\n\n private generation = 0\n private searchTimer?: ReturnType<typeof setTimeout>\n\n /** Read the list again - the host calls this after it opened or started a conversation. */\n async reload(): Promise<void> {\n const generation = ++this.generation\n if (!this.anchorType || !this.anchorId) {\n this.entries = []\n this.total = this.hiddenCount = 0\n return\n }\n\n this.loading = true\n this.error = ''\n try {\n /*\n * Paging reads again from the start rather than appending - live activity reorders the list, and\n * appending a later page to an earlier order duplicates and drops rows.\n */\n const page = await this.admin.inbox({\n anchorType: this.anchorType,\n anchorId: this.anchorId,\n search: this.search,\n filters: {\n kind: this.kind,\n whose: this.whose,\n visibility: this.visibility,\n includeHidden: this.includeHidden,\n unreadOnly: this.unreadOnly\n },\n limit: this.pageSize,\n offset: 0\n })\n if (generation !== this.generation) return\n this.entries = page.entries\n this.total = page.total\n this.hiddenCount = page.hiddenCount\n } catch (error) {\n if (generation !== this.generation) return\n this.entries = []\n this.total = this.hiddenCount = 0\n this.error = (error as Error).message || t('failed', 'could not load the conversation list.')\n } finally {\n if (generation === this.generation) this.loading = false\n }\n }\n\n protected updated(changed: PropertyValues) {\n if (changed.has('anchorType') || changed.has('anchorId') || changed.has('admin') || changed.has('kind')) void this.reload()\n\n /*\n * The rename box takes the keys the moment it opens, with the name selected. `autofocus` works once\n * per document, so the second rename opened a box that ignored the typing (2026-09-24, operato-ecs).\n */\n if (changed.has('renamingId') && this.renamingId) {\n const input = this.renderRoot.querySelector('input.rename') as HTMLInputElement | null\n input?.focus()\n input?.select()\n }\n }\n\n disconnectedCallback() {\n this.generation++\n clearTimeout(this.searchTimer)\n super.disconnectedCallback()\n }\n\n private emit(name: string, detail?: unknown) {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }))\n }\n\n /* ── what the person does ────────────────────────────────────────────────────────── */\n\n private onSearch(value: string) {\n this.search = value\n clearTimeout(this.searchTimer)\n /* Not on every keystroke. A new search starts from the first page. */\n this.searchTimer = setTimeout(() => {\n this.pageSize = PAGE\n void this.reload()\n }, 200)\n }\n\n /** A filter changed - the list starts from its first page again. */\n private refilter(change: () => void) {\n change()\n this.pageSize = PAGE\n void this.reload()\n }\n\n private loadMore() {\n this.pageSize += PAGE\n void this.reload()\n }\n\n /** Runs one door, then reads the list again - on success and on failure alike, so the row shows the truth. */\n private async act(entry: AssistantSessionEntry, change: string, run: () => Promise<boolean>, refused: string) {\n this.renamingId = ''\n try {\n if (!(await run())) this.error = refused\n else this.emit('assistant-session-changed', { sessionId: entry.id, change })\n } catch (error) {\n /* Never pass a failure silently - a person must not believe a conversation was deleted when it was not. */\n this.error = (error as Error).message\n }\n const error = this.error\n await this.reload()\n if (error && !this.error) this.error = error\n }\n\n private rename(entry: AssistantSessionEntry, name: string) {\n const trimmed = name.trim()\n this.renamingId = ''\n if (!trimmed || trimmed === entry.name) return\n void this.act(entry, 'renamed', () => this.admin.rename(entry.id, trimmed), t('rename-refused', 'this conversation could not be renamed.'))\n }\n\n private setHidden(entry: AssistantSessionEntry, hidden: boolean) {\n void this.act(entry, hidden ? 'hidden' : 'unhidden', () => this.admin.hide(entry.id, hidden), t('hide-refused', 'this conversation could not be hidden.'))\n }\n\n private setVisibility(entry: AssistantSessionEntry, visibility: 'open' | 'private') {\n void this.act(\n entry,\n 'visibility',\n () => this.admin.setVisibility(entry.id, visibility),\n t('visibility-refused', 'only the owner of this conversation can change who reads it.')\n )\n }\n\n private _removeSession(entry: AssistantSessionEntry) {\n void this.act(entry, 'removed', () => this.admin.remove(entry.id), t('remove-refused', 'this conversation could not be deleted.'))\n }\n\n /* ── drawing ─────────────────────────────────────────────────────────────────────── */\n\n render(): TemplateResult {\n const shown = this.entries.length\n\n return html`\n <div class=\"bar\">\n <input\n .value=${this.search}\n placeholder=${t('search', 'Search conversations')}\n aria-label=${t('search', 'Search conversations')}\n @input=${(e: Event) => this.onSearch((e.target as HTMLInputElement).value)}\n @keydown=${(e: KeyboardEvent) => { if (e.key === 'Escape' && this.search) { e.stopPropagation(); this.onSearch('') } }}\n />\n <button class=\"new\" @click=${() => this.emit('assistant-session-reset')}>+ ${t('new', 'New conversation')}</button>\n </div>\n <div class=\"filters\">\n <select aria-label=${t('filter-whose', 'Whose')} .value=${this.whose}\n @change=${(e: Event) => this.refilter(() => (this.whose = (e.target as HTMLSelectElement).value as any))}>\n <option value=\"all\">${t('filter-all', 'All')}</option>\n <option value=\"mine\">${t('filter-mine', 'Started by me')}</option>\n <option value=\"participant\">${t('filter-participant', \"I'm in\")}</option>\n </select>\n <select aria-label=${t('filter-visibility', 'Visibility')} .value=${this.visibility}\n @change=${(e: Event) => this.refilter(() => (this.visibility = (e.target as HTMLSelectElement).value as any))}>\n <option value=\"all\">${t('filter-any-visibility', 'Public and private')}</option>\n <option value=\"open\">${t('filter-open', 'Public')}</option>\n <option value=\"private\">${t('filter-private', 'Private')}</option>\n </select>\n <label><input type=\"checkbox\" .checked=${this.unreadOnly}\n @change=${(e: Event) => this.refilter(() => (this.unreadOnly = (e.target as HTMLInputElement).checked))} />${t('filter-unread', 'Unread')}</label>\n <label><input type=\"checkbox\" .checked=${this.includeHidden}\n @change=${(e: Event) => this.refilter(() => (this.includeHidden = (e.target as HTMLInputElement).checked))} />${t('filter-hidden', 'Including hidden')}</label>\n ${this.filtering ? html`<button class=\"clear\" @click=${() => this.clearFilters()}>${t('reset-filters', 'Clear search and filters')}</button>` : nothing}\n </div>\n <div class=\"list\">\n ${this.error ? html`<div class=\"note\" role=\"alert\">${this.error}</div>` : nothing}\n ${this.loading ? html`<div class=\"note\" role=\"status\">${t('loading', 'Loading conversations…')}</div>` : nothing}\n ${shown === 0 && !this.loading && !this.error\n ? html`<div class=\"empty\" role=\"status\">${this.filtering ? t('filtered-empty', 'No conversations match these filters here.') : t('scope-empty', 'No conversations yet.')}</div>`\n : this.entries.map(entry => this.renderRow(entry))}\n ${shown < this.total\n ? html`<button class=\"more\" @click=${() => this.loadMore()}>${t('load-more', 'Show more ({rest})', { rest: this.total - shown })}</button>`\n : nothing}\n </div>\n <!-- A hidden conversation must not be forgotten: say how many, and one press shows them. -->\n ${this.hiddenCount > 0 && !this.includeHidden\n ? html`<button class=\"hiddenfilter\" @click=${() => this.refilter(() => (this.includeHidden = true))}>\n <md-icon>visibility_off</md-icon>${t('hidden-count', '{count} hidden', { count: this.hiddenCount })}\n </button>`\n : nothing}\n `\n }\n\n private renderRow(entry: AssistantSessionEntry): TemplateResult {\n const name = entry.name || t('untitled', 'Untitled conversation')\n const actions = sessionActions(entry)\n const isPrivate = entry.visibility === 'private'\n\n if (this.renamingId === entry.id) {\n return html`<div class=\"row\">\n <span class=\"dot\"></span>\n <input\n class=\"rename\"\n .value=${entry.name || ''}\n aria-label=${t('rename', 'Rename')}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter') this.rename(entry, (e.target as HTMLInputElement).value)\n if (e.key === 'Escape') { e.stopPropagation(); this.renamingId = '' }\n }}\n @blur=${(e: Event) => this.rename(entry, (e.target as HTMLInputElement).value)}\n />\n </div>`\n }\n\n return html`<div\n class=\"row ${entry.unread ? 'unread' : ''} ${entry.hidden ? 'hidden' : ''} ${entry.id === this.sessionId ? 'current' : ''} ${isPrivate ? '' : 'open-audience'}\"\n >\n <span class=\"dot\"></span>\n <button class=\"open-row\" aria-current=${entry.id === this.sessionId ? 'true' : 'false'} @click=${() => this.emit('assistant-session-select', { sessionId: entry.id, kind: entry.kind })}>\n <div class=\"title\">${name}</div>\n <div class=\"prev\">${entry.lastMessagePreview || ''}</div>\n <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>\n </button>\n <button\n class=\"more-menu\"\n aria-label=${t('manage', 'Manage conversation')}\n title=${t('manage', 'Manage conversation')}\n @click=${(e: MouseEvent) => this.openRowMenu(e, entry, name)}>⋯</button>\n </div>`\n }\n\n /**\n * A row's actions in the platform's popup menu - one open at a time, closed by a press outside or Escape, placed\n * inside the window. The hand-made menu it replaces stayed open and stacked over the next rows.\n */\n private openRowMenu(event: MouseEvent, entry: AssistantSessionEntry, name: string) {\n event.stopPropagation()\n const actions = sessionActions(entry)\n const isPrivate = entry.visibility === 'private'\n const at = (event.currentTarget as HTMLElement).getBoundingClientRect()\n OxPopupMenu.open({\n template: html`\n ${actions.rename ? html`<ox-popup-menuitem label=${t('rename', 'Rename')} @select=${() => (this.renamingId = entry.id)}></ox-popup-menuitem>` : nothing}\n ${actions.setVisibility\n ? isPrivate\n ? html`<ox-popup-menuitem label=${t('make-open', 'Make public')} @select=${() => this.askOpen(entry, name)}></ox-popup-menuitem>`\n : html`<ox-popup-menuitem label=${t('make-private', 'Make private')} @select=${() => this.setVisibility(entry, 'private')}></ox-popup-menuitem>`\n : nothing}\n <ox-popup-menuitem\n label=${actions.unhide ? t('unhide', 'Show in my list') : t('hide', 'Hide from my list')}\n @select=${() => this.setHidden(entry, !entry.hidden)}></ox-popup-menuitem>\n ${actions.remove ? html`<ox-popup-menuitem label=${t('remove', 'Delete')} @select=${() => this.askRemove(entry, name)}></ox-popup-menuitem>` : nothing}\n `,\n top: at.bottom,\n right: window.innerWidth - at.right,\n parent: document.body\n })\n }\n\n /** Before what cannot be taken back - the platform's confirm, [Cancel][OK] (decided 2026-09-25, §7-3). */\n private async ask(title: string, text: string, confirmText: string): Promise<boolean> {\n return !!(await OxPrompt.open({\n type: 'warning',\n title,\n text,\n confirmButton: { text: confirmText },\n cancelButton: { text: t('cancel', 'Cancel') }\n }))\n }\n\n private async askRemove(entry: AssistantSessionEntry, name: string) {\n const ok = await this.ask(\n t('remove', 'Delete'),\n t('confirm-remove', 'Delete \"{name}\"? It disappears for every participant and cannot be brought back.', { name }),\n t('remove', 'Delete')\n )\n if (ok) this._removeSession(entry)\n }\n\n private async askOpen(entry: AssistantSessionEntry, name: string) {\n const ok = await this.ask(\n t('make-open', 'Make public'),\n t('confirm-open', 'Make \"{name}\" public? Everyone in this domain will be able to read what is already written.', { name }),\n t('make-open', 'Make public')\n )\n if (ok) this.setVisibility(entry, 'open')\n }\n}\n"]}
@@ -15,5 +15,6 @@ export declare class AssistantSessionToolbar extends LitElement {
15
15
  compact: boolean;
16
16
  static styles: import("lit").CSSResult;
17
17
  private emit;
18
+ private get currentName();
18
19
  render(): import("lit-html").TemplateResult<1>;
19
20
  }
@@ -31,6 +31,9 @@ let AssistantSessionToolbar = 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; }
@@ -45,20 +48,24 @@ let AssistantSessionToolbar = class AssistantSessionToolbar extends LitElement {
45
48
  emit(name, detail) {
46
49
  this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
47
50
  }
51
+ get currentName() {
52
+ return this.sessions.find(session => session.id === this.sessionId)?.name || i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' });
53
+ }
48
54
  render() {
49
55
  const label = i18next.t('ai-assistant.session.history', { defaultValue: 'Conversation history' });
50
56
  return html `
51
57
  <div class="controls">
52
- ${this.singleConversation ? html `<button @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.list', { defaultValue: 'Conversations' })}</button><span class="tabs">${this.sessions.find(session => session.id === this.sessionId)?.name || i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</span>` : nothing}
58
+ ${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}
53
59
  ${this.temporary || this.singleConversation ? nothing : html `<div class="tabs" role="tablist" aria-label=${label}>
54
60
  ${!this.sessionId ? html `<button role="tab" aria-selected="true">${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}
55
61
  ${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'}
56
62
  ?disabled=${this.disabled || this.loading} title=${session.name || `${label} ${index + 1}`}
57
63
  @click=${() => this.emit('assistant-session-select', { sessionId: session.id })}>${session.name || `${label} ${index + 1}`}</button>`)}
58
64
  </div>`}
59
- <button class="session-new" ?disabled=${this.disabled || this.loading || !!this.error}
65
+ <!-- A conversation not yet started is already the new one - offering another names it twice. -->
66
+ ${this.sessionId ? html `<button class="session-new" ?disabled=${this.disabled || this.loading || !!this.error}
60
67
  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.' })}
61
- @click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>
68
+ @click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}
62
69
  </div>
63
70
  ${this.historyEnabled && !this.singleConversation ? html `<button class="history" ?disabled=${this.loading || this.disabled}
64
71
  @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.all', { defaultValue: 'All History' })}</button>` : nothing}
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-session-toolbar.js","sourceRoot":"","sources":["../../client/components/assistant-session-toolbar.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { i18next } from '@operato/i18n'\nimport type { AssistantSession } from '../utils/assistant-session-controller'\nimport { assistantRecentSessions } from '../utils/assistant-request-context.js'\n\n/** Product-neutral controls. The host/controller owns effects; this view never mutates sessions. */\n@customElement('ox-assistant-session-toolbar')\nexport class AssistantSessionToolbar extends LitElement {\n @property({ attribute: false }) sessions: AssistantSession[] = []\n @property() sessionId = ''\n @property({ type: Boolean }) loading = false\n @property({ type: Boolean }) disabled = false\n @property({ type: Boolean }) temporary = false\n @property() error = ''\n @property({ type: Boolean }) historyEnabled = false\n @property({ type: Boolean }) singleConversation = false\n @property({ type: Number }) maxTabs = 5\n /** 좁은 도크의 문맥 헤더 바로 아래에서 쓰는 밀도. 기능은 줄이지 않는다. */\n @property({ type: Boolean, reflect: true }) compact = false\n\n static styles = css`\n :host { display: block; min-width: 0; }\n .controls { display: flex; align-items: center; gap: 6px; }\n button { box-sizing: border-box; height: 28px; font: inherit; font-size: 12px;\n color: var(--secondary-color, #68747c); border: 0; border-radius: 4px;\n background: transparent; cursor: pointer; }\n .tabs { display:flex; flex:1; min-width:0; overflow-x:auto; gap:1px; scrollbar-width:thin; }\n [role=tab] { max-width:160px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;\n padding: 0 8px; border-bottom: 2px solid transparent; border-radius: 3px 3px 0 0; }\n [role=tab]:hover { background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); color: var(--primary-color, #007f87); }\n [aria-selected=true] { color:var(--primary-color, #007f87); border-bottom-color: currentColor; font-weight:600; }\n .session-new { flex: none; padding: 0 7px; font-weight: 500; white-space: nowrap; }\n .session-new:hover, .history:hover { color: var(--primary-color, #007f87); background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); }\n .history { height: 24px; margin: 1px 0 0 4px; padding: 0 5px; font-size: 11px; color: var(--tertiary-color, #88939a); }\n :host([compact]) .controls { gap: 3px; min-height: 24px; }\n :host([compact]) button { height: 24px; font-size: 11px; }\n :host([compact]) [role=tab] { max-width: 132px; padding: 0 6px; border-bottom-width: 1px; }\n :host([compact]) .session-new { padding: 0 5px; }\n :disabled { opacity: .5; cursor: default; }\n :focus-visible { outline: 2px solid var(--primary-color, #007f87); outline-offset: 2px; }\n p { margin: 6px 0 0; font-size: 12px; overflow-wrap: anywhere; }\n [role=alert] { color: var(--error-color, #b42318); }\n `\n\n private emit(name: string, detail?: unknown) {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }))\n }\n\n render() {\n const label = i18next.t('ai-assistant.session.history', { defaultValue: 'Conversation history' })\n return html`\n <div class=\"controls\">\n ${this.singleConversation ? html`<button @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.list', { defaultValue: 'Conversations' })}</button><span class=\"tabs\">${this.sessions.find(session => session.id === this.sessionId)?.name || i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</span>` : nothing}\n ${this.temporary || this.singleConversation ? nothing : html`<div class=\"tabs\" role=\"tablist\" aria-label=${label}>\n ${!this.sessionId ? html`<button role=\"tab\" aria-selected=\"true\">${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}\n ${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'}\n ?disabled=${this.disabled || this.loading} title=${session.name || `${label} ${index + 1}`}\n @click=${() => this.emit('assistant-session-select', { sessionId: session.id })}>${session.name || `${label} ${index + 1}`}</button>`)}\n </div>`}\n <button class=\"session-new\" ?disabled=${this.disabled || this.loading || !!this.error}\n 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.' })}\n @click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>\n </div>\n ${this.historyEnabled && !this.singleConversation ? html`<button class=\"history\" ?disabled=${this.loading || this.disabled}\n @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.all', { defaultValue: 'All History' })}</button>` : nothing}\n ${this.loading ? html`<p role=\"status\">${i18next.t('ai-assistant.session.loading', { defaultValue: 'Loading conversation history…' })}</p>` : nothing}\n ${this.error ? html`<p role=\"alert\">${this.error} <button ?disabled=${this.loading || this.disabled}\n @click=${() => this.emit('assistant-session-retry')}>${i18next.t('ai-assistant.session.retry', { defaultValue: 'Retry' })}</button></p>` : nothing}\n `\n }\n}\n"]}
1
+ {"version":3,"file":"assistant-session-toolbar.js","sourceRoot":"","sources":["../../client/components/assistant-session-toolbar.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { i18next } from '@operato/i18n'\nimport type { AssistantSession } from '../utils/assistant-session-controller'\nimport { assistantRecentSessions } from '../utils/assistant-request-context.js'\n\n/** Product-neutral controls. The host/controller owns effects; this view never mutates sessions. */\n@customElement('ox-assistant-session-toolbar')\nexport class AssistantSessionToolbar extends LitElement {\n @property({ attribute: false }) sessions: AssistantSession[] = []\n @property() sessionId = ''\n @property({ type: Boolean }) loading = false\n @property({ type: Boolean }) disabled = false\n @property({ type: Boolean }) temporary = false\n @property() error = ''\n @property({ type: Boolean }) historyEnabled = false\n @property({ type: Boolean }) singleConversation = false\n @property({ type: Number }) maxTabs = 5\n /** 좁은 도크의 문맥 헤더 바로 아래에서 쓰는 밀도. 기능은 줄이지 않는다. */\n @property({ type: Boolean, reflect: true }) compact = false\n\n static styles = css`\n :host { display: block; min-width: 0; }\n .controls { display: flex; align-items: center; gap: 6px; }\n button { box-sizing: border-box; height: 28px; font: inherit; font-size: 12px;\n color: var(--secondary-color, #68747c); border: 0; border-radius: 4px;\n background: transparent; cursor: pointer; }\n .tabs { display:flex; flex:1; min-width:0; overflow-x:auto; gap:1px; scrollbar-width:thin; }\n [role=tab] { max-width:160px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;\n padding: 0 8px; border-bottom: 2px solid transparent; border-radius: 3px 3px 0 0; }\n [role=tab]:hover { background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); color: var(--primary-color, #007f87); }\n [aria-selected=true] { color:var(--primary-color, #007f87); border-bottom-color: currentColor; font-weight:600; }\n .session-new { flex: none; padding: 0 7px; font-weight: 500; white-space: nowrap; }\n /* The open conversation's name is one line - a long name is cut, and the whole of it is the tooltip. */\n .list { flex: none; white-space: nowrap; }\n .current { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }\n .session-new:hover, .history:hover { color: var(--primary-color, #007f87); background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); }\n .history { height: 24px; margin: 1px 0 0 4px; padding: 0 5px; font-size: 11px; color: var(--tertiary-color, #88939a); }\n :host([compact]) .controls { gap: 3px; min-height: 24px; }\n :host([compact]) button { height: 24px; font-size: 11px; }\n :host([compact]) [role=tab] { max-width: 132px; padding: 0 6px; border-bottom-width: 1px; }\n :host([compact]) .session-new { padding: 0 5px; }\n :disabled { opacity: .5; cursor: default; }\n :focus-visible { outline: 2px solid var(--primary-color, #007f87); outline-offset: 2px; }\n p { margin: 6px 0 0; font-size: 12px; overflow-wrap: anywhere; }\n [role=alert] { color: var(--error-color, #b42318); }\n `\n\n private emit(name: string, detail?: unknown) {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }))\n }\n\n private get currentName(): string {\n return this.sessions.find(session => session.id === this.sessionId)?.name || i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })\n }\n\n render() {\n const label = i18next.t('ai-assistant.session.history', { defaultValue: 'Conversation history' })\n return html`\n <div class=\"controls\">\n ${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}\n ${this.temporary || this.singleConversation ? nothing : html`<div class=\"tabs\" role=\"tablist\" aria-label=${label}>\n ${!this.sessionId ? html`<button role=\"tab\" aria-selected=\"true\">${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}\n ${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'}\n ?disabled=${this.disabled || this.loading} title=${session.name || `${label} ${index + 1}`}\n @click=${() => this.emit('assistant-session-select', { sessionId: session.id })}>${session.name || `${label} ${index + 1}`}</button>`)}\n </div>`}\n <!-- A conversation not yet started is already the new one - offering another names it twice. -->\n ${this.sessionId ? html`<button class=\"session-new\" ?disabled=${this.disabled || this.loading || !!this.error}\n 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.' })}\n @click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: 'New conversation' })}</button>` : nothing}\n </div>\n ${this.historyEnabled && !this.singleConversation ? html`<button class=\"history\" ?disabled=${this.loading || this.disabled}\n @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.all', { defaultValue: 'All History' })}</button>` : nothing}\n ${this.loading ? html`<p role=\"status\">${i18next.t('ai-assistant.session.loading', { defaultValue: 'Loading conversation history…' })}</p>` : nothing}\n ${this.error ? html`<p role=\"alert\">${this.error} <button ?disabled=${this.loading || this.disabled}\n @click=${() => this.emit('assistant-session-retry')}>${i18next.t('ai-assistant.session.retry', { defaultValue: 'Retry' })}</button></p>` : nothing}\n `\n }\n}\n"]}
@@ -54,6 +54,8 @@ export interface ChatMutationInputArgs {
54
54
  kind: string;
55
55
  target?: string;
56
56
  };
57
+ /** The AI agent the person picked. Omitted, the domain default answers. */
58
+ agentId?: string;
57
59
  }
58
60
  /**
59
61
  * GraphQL mutation 의 input 객체를 빌드.
@@ -89,6 +91,8 @@ export interface AssistantChatInputArgs {
89
91
  hostContext?: any;
90
92
  /** Files stored with the conversation by attachToConversation, sent with this turn. */
91
93
  attachmentIds?: string[];
94
+ /** The AI agent the person picked. Omitted, the domain default answers. */
95
+ agentId?: string;
92
96
  }
93
97
  /**
94
98
  * Build the neutral input.
@@ -27,7 +27,8 @@ export function buildChatMutationInput(args) {
27
27
  componentSchemas: args.componentSchemas,
28
28
  selectedRefids: args.selectedRefids,
29
29
  ...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions }),
30
- ...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {})
30
+ ...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {}),
31
+ ...(args.agentId ? { agentId: args.agentId } : {})
31
32
  };
32
33
  }
33
34
  /**
@@ -48,7 +49,8 @@ export function buildAssistantChatInput(args) {
48
49
  mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) },
49
50
  ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),
50
51
  ...(args.hostContext ? { hostContext: args.hostContext } : {}),
51
- ...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {})
52
+ ...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {}),
53
+ ...(args.agentId ? { agentId: args.agentId } : {})
52
54
  };
53
55
  }
54
56
  //# sourceMappingURL=chat-input-builder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-input-builder.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA2B;IAChE,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,YAAY,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACpC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,qDAAqD;QACrD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7E,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1H,CAAA;AACH,CAAC;AAyBD;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAA4B;IAClE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,kGAAkG;YAChG,kEAAkE,CACrE,CAAA;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;QACzF,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAA;AACH,CAAC","sourcesContent":["/**\n * boardAIChat GraphQL mutation 의 input 객체 빌더.\n *\n * board-ai-chat.ts (Lit / Material Web 의존) 와 분리 — pure 함수만 두면 jest 의 node\n * 환경에서도 import 가능. 회귀 테스트 (selectedRefids 누락 등) 가 단위 테스트로 잠긴다.\n */\n\nexport interface ChatMutationInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n liveBoard: any\n scopes?: string[]\n /** 편집으로 접은 지점(마지막으로 남긴 서버 메시지 id) — 서버가 그 뒤 이력을 에이전트 문맥에서 뺀다. */\n truncateAfterMessageId?: string\n /** 이 대화면이 무엇을 보고 있는지(예: { spaceId }) — 도구가 대상을 알 수 있게. 없으면 모델이 식별자를 되묻는다. */\n hostContext?: any\n /**\n * 이 대화면이 쓸 수 있는 외부 도구 카테고리(registerToolCategory 이름).\n * 미지정 = 등록된 전부(현행 동작). 지정하면 정의와 **실행** 모두 그 범위로 제한된다.\n */\n toolCategories?: string[]\n /** 코어 보드 편집 도구(addComponent·스타일링·씬 조작) 사용 여부. 기본 true. */\n boardTools?: boolean\n /**\n * 첫 턴에 도구 호출을 강제할지 — 라이브 상태를 다루는 대화면에서 켠다.\n * 근거 없이 상태를 단언하는 답(도구를 아예 부르지 않은 답)을 원천에서 막는다.\n */\n requireGroundingTools?: boolean\n knownTypes?: string[]\n categories?: string[]\n componentSchemas?: any\n /**\n * 선택된 컴포넌트의 `refid` 배열 — things-scene universal numeric handle.\n * id (데이터 바인딩 이름) 와는 다른 개념.\n */\n selectedRefids: number[]\n /**\n * popup 에서 명시 선택한 #mention 매핑 — {token, refid}.\n * 사용자 textarea 에는 #token 만 보이고, 정확한 refid 는 이 별도 채널로 server 에 전달.\n * server 가 mention map 으로 LLM 에 정확한 refid 정보 첨부 (사용자 노출 0).\n */\n mentions?: Array<{ token: string; refid: number }>\n /**\n * The registered conversation this board chat is (ADR-0061 decision 7). When sent, the server\n * takes the persona, tool categories, model and limits from that mode and ignores the per-request\n * ones above. Omitted for a surface that declares no mode.\n */\n mode?: { kind: string; target?: string }\n}\n\n/**\n * GraphQL mutation 의 input 객체를 빌드.\n *\n * 핵심 보호 — `selectedRefids` 가 항상 포함되어야 한다. 누락이 사용자에게 보고된\n * \"AI 가 선택된 컴포넌트를 모른다\" 의 root cause 였다.\n */\nexport function buildChatMutationInput(args: ChatMutationInputArgs): Record<string, any> {\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n currentBoard: args.liveBoard ?? null,\n scopes: args.scopes,\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n /* 미지정은 서버 기본(전부 허용)에 맡긴다 — null 을 보내 굳이 덮어쓰지 않는다. */\n ...(args.toolCategories ? { toolCategories: args.toolCategories } : {}),\n ...(args.boardTools === false ? { boardTools: false } : {}),\n ...(args.requireGroundingTools ? { requireGroundingTools: true } : {}),\n knownTypes: args.knownTypes,\n categories: args.categories,\n componentSchemas: args.componentSchemas,\n selectedRefids: args.selectedRefids,\n ...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions }),\n ...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {})\n }\n}\n\n/**\n * Input for `assistantChat` — the door for a surface that has no document of its own.\n *\n * Deliberately a separate builder rather than a flag on the one above. The two mutations take\n * different inputs, and folding them together would mean a board field reaching a surface that\n * has no board, or a required field going missing with nothing to notice it. The one thing they\n * share is the conversation, and that is passed the same way.\n */\nexport interface AssistantChatInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n /**\n * Which conversation this is and what it is about. The persona, the tool categories, the model\n * and the limits belong to the mode the app server registered (ADR-0061 decision 7) — a caller\n * that could choose its own tool categories was a caller choosing its own authority.\n */\n mode: { kind: string; target?: string }\n truncateAfterMessageId?: string\n hostContext?: any\n /** Files stored with the conversation by attachToConversation, sent with this turn. */\n attachmentIds?: string[]\n}\n\n/**\n * Build the neutral input.\n *\n * Refuses a conversation with no mode instead of sending one. A turn with no mode has no persona\n * and no tools, and an answer given as nobody reads like a bad model rather than a missing\n * argument — the server refuses it too, and this says so where the surface is.\n */\nexport function buildAssistantChatInput(args: AssistantChatInputArgs): Record<string, any> {\n if (!args.mode?.kind?.trim()) {\n throw new Error(\n '[ox-assistant-chat] mode.kind is required when the chat talks to assistantChat — the app server ' +\n 'registers the mode and the screen names it (ADR-0061 decision 7)'\n )\n }\n\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) },\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n ...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {})\n }\n}\n"]}
1
+ {"version":3,"file":"chat-input-builder.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * boardAIChat GraphQL mutation 의 input 객체 빌더.\n *\n * board-ai-chat.ts (Lit / Material Web 의존) 와 분리 — pure 함수만 두면 jest 의 node\n * 환경에서도 import 가능. 회귀 테스트 (selectedRefids 누락 등) 가 단위 테스트로 잠긴다.\n */\n\nexport interface ChatMutationInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n liveBoard: any\n scopes?: string[]\n /** 편집으로 접은 지점(마지막으로 남긴 서버 메시지 id) — 서버가 그 뒤 이력을 에이전트 문맥에서 뺀다. */\n truncateAfterMessageId?: string\n /** 이 대화면이 무엇을 보고 있는지(예: { spaceId }) — 도구가 대상을 알 수 있게. 없으면 모델이 식별자를 되묻는다. */\n hostContext?: any\n /**\n * 이 대화면이 쓸 수 있는 외부 도구 카테고리(registerToolCategory 이름).\n * 미지정 = 등록된 전부(현행 동작). 지정하면 정의와 **실행** 모두 그 범위로 제한된다.\n */\n toolCategories?: string[]\n /** 코어 보드 편집 도구(addComponent·스타일링·씬 조작) 사용 여부. 기본 true. */\n boardTools?: boolean\n /**\n * 첫 턴에 도구 호출을 강제할지 — 라이브 상태를 다루는 대화면에서 켠다.\n * 근거 없이 상태를 단언하는 답(도구를 아예 부르지 않은 답)을 원천에서 막는다.\n */\n requireGroundingTools?: boolean\n knownTypes?: string[]\n categories?: string[]\n componentSchemas?: any\n /**\n * 선택된 컴포넌트의 `refid` 배열 — things-scene universal numeric handle.\n * id (데이터 바인딩 이름) 와는 다른 개념.\n */\n selectedRefids: number[]\n /**\n * popup 에서 명시 선택한 #mention 매핑 — {token, refid}.\n * 사용자 textarea 에는 #token 만 보이고, 정확한 refid 는 이 별도 채널로 server 에 전달.\n * server 가 mention map 으로 LLM 에 정확한 refid 정보 첨부 (사용자 노출 0).\n */\n mentions?: Array<{ token: string; refid: number }>\n /**\n * The registered conversation this board chat is (ADR-0061 decision 7). When sent, the server\n * takes the persona, tool categories, model and limits from that mode and ignores the per-request\n * ones above. Omitted for a surface that declares no mode.\n */\n mode?: { kind: string; target?: string }\n /** The AI agent the person picked. Omitted, the domain default answers. */\n agentId?: string\n}\n\n/**\n * GraphQL mutation 의 input 객체를 빌드.\n *\n * 핵심 보호 — `selectedRefids` 가 항상 포함되어야 한다. 누락이 사용자에게 보고된\n * \"AI 가 선택된 컴포넌트를 모른다\" 의 root cause 였다.\n */\nexport function buildChatMutationInput(args: ChatMutationInputArgs): Record<string, any> {\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n currentBoard: args.liveBoard ?? null,\n scopes: args.scopes,\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n /* 미지정은 서버 기본(전부 허용)에 맡긴다 — null 을 보내 굳이 덮어쓰지 않는다. */\n ...(args.toolCategories ? { toolCategories: args.toolCategories } : {}),\n ...(args.boardTools === false ? { boardTools: false } : {}),\n ...(args.requireGroundingTools ? { requireGroundingTools: true } : {}),\n knownTypes: args.knownTypes,\n categories: args.categories,\n componentSchemas: args.componentSchemas,\n selectedRefids: args.selectedRefids,\n ...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions }),\n ...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {})\n }\n}\n\n/**\n * Input for `assistantChat` — the door for a surface that has no document of its own.\n *\n * Deliberately a separate builder rather than a flag on the one above. The two mutations take\n * different inputs, and folding them together would mean a board field reaching a surface that\n * has no board, or a required field going missing with nothing to notice it. The one thing they\n * share is the conversation, and that is passed the same way.\n */\nexport interface AssistantChatInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n /**\n * Which conversation this is and what it is about. The persona, the tool categories, the model\n * and the limits belong to the mode the app server registered (ADR-0061 decision 7) — a caller\n * that could choose its own tool categories was a caller choosing its own authority.\n */\n mode: { kind: string; target?: string }\n truncateAfterMessageId?: string\n hostContext?: any\n /** Files stored with the conversation by attachToConversation, sent with this turn. */\n attachmentIds?: string[]\n /** The AI agent the person picked. Omitted, the domain default answers. */\n agentId?: string\n}\n\n/**\n * Build the neutral input.\n *\n * Refuses a conversation with no mode instead of sending one. A turn with no mode has no persona\n * and no tools, and an answer given as nobody reads like a bad model rather than a missing\n * argument — the server refuses it too, and this says so where the surface is.\n */\nexport function buildAssistantChatInput(args: AssistantChatInputArgs): Record<string, any> {\n if (!args.mode?.kind?.trim()) {\n throw new Error(\n '[ox-assistant-chat] mode.kind is required when the chat talks to assistantChat — the app server ' +\n 'registers the mode and the screen names it (ADR-0061 decision 7)'\n )\n }\n\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) },\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n ...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {})\n }\n}\n"]}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * What the AI agent card shows and sends, apart from how it draws (design/ai/agent-settings.md §7).
3
+ *
4
+ * Every action on the card - pick the default, turn an agent off, edit one, delete one, answer an inherited agent with
5
+ * this domain's key - is saved as the domain's whole agent list, so the server checks it as one write.
6
+ */
7
+ export interface AgentDomain {
8
+ id: string;
9
+ name: string;
10
+ }
11
+ export interface AgentView {
12
+ id: string;
13
+ name: string;
14
+ provider: string;
15
+ model: string;
16
+ baseURL?: string | null;
17
+ organization?: string | null;
18
+ active: boolean;
19
+ owner: AgentDomain;
20
+ inherited: boolean;
21
+ turnedOff: boolean;
22
+ keyState: 'own' | 'inherited' | 'overridden' | 'missing';
23
+ keyDomain?: AgentDomain | null;
24
+ keyHint?: string | null;
25
+ usable: boolean;
26
+ isDefault: boolean;
27
+ usedBelow: AgentDomain[];
28
+ }
29
+ export interface AgentSettings {
30
+ chain: AgentDomain[];
31
+ agents: AgentView[];
32
+ defaultAgentId?: string | null;
33
+ defaultFrom: 'own' | 'inherited' | 'first' | 'none';
34
+ state: string;
35
+ reason?: string | null;
36
+ }
37
+ export interface AgentDraft {
38
+ id?: string;
39
+ name: string;
40
+ provider: string;
41
+ model: string;
42
+ baseURL: string;
43
+ organization: string;
44
+ active: boolean;
45
+ /** A new key. Empty keeps the stored one. */
46
+ apiKey: string;
47
+ }
48
+ export type SaveChange = {
49
+ kind: 'default';
50
+ agentId: string;
51
+ } | {
52
+ kind: 'turn-off';
53
+ agentId: string;
54
+ off: boolean;
55
+ } | {
56
+ kind: 'active';
57
+ agentId: string;
58
+ active: boolean;
59
+ } | {
60
+ kind: 'delete';
61
+ agentId: string;
62
+ } | {
63
+ kind: 'upsert';
64
+ draft: AgentDraft;
65
+ } | {
66
+ kind: 'key-override';
67
+ agentId: string;
68
+ apiKey: string;
69
+ } | {
70
+ kind: 'key-clear';
71
+ agentId: string;
72
+ };
73
+ export declare function draftOf(agent?: AgentView): AgentDraft;
74
+ export interface AgentGroup {
75
+ ownerId: string;
76
+ inherited: boolean;
77
+ /** The owner's place: `그룹 › 본사` when there is more than one domain above, else its name. */
78
+ path: string;
79
+ agents: AgentView[];
80
+ }
81
+ /** This domain's own agents first, then each domain above, closest first. A domain with no agents has no group. */
82
+ export declare function groupByOwner(settings: AgentSettings): AgentGroup[];
83
+ /** The whole save for one action on the card. */
84
+ export declare function saveInput(settings: AgentSettings, change: SaveChange): {
85
+ inheritedKeys?: {
86
+ agentId: string;
87
+ apiKey?: string;
88
+ clear?: boolean;
89
+ }[] | undefined;
90
+ turnedOff: string[];
91
+ defaultAgentId?: string | undefined;
92
+ agents: Record<string, any>[];
93
+ };
@@ -0,0 +1,95 @@
1
+ export function draftOf(agent) {
2
+ return {
3
+ ...(agent && { id: agent.id }),
4
+ name: agent?.name ?? '',
5
+ provider: agent?.provider ?? 'openai',
6
+ model: agent?.model ?? '',
7
+ baseURL: agent?.baseURL ?? '',
8
+ organization: agent?.organization ?? '',
9
+ active: agent?.active ?? true,
10
+ apiKey: ''
11
+ };
12
+ }
13
+ /** This domain's own agents first, then each domain above, closest first. A domain with no agents has no group. */
14
+ export function groupByOwner(settings) {
15
+ const groups = [];
16
+ const above = settings.chain.length - 1;
17
+ for (const [index, domain] of settings.chain.entries()) {
18
+ const agents = settings.agents.filter(one => one.owner.id === domain.id);
19
+ if (!agents.length && index > 0)
20
+ continue;
21
+ const path = above > 1
22
+ ? settings.chain
23
+ .slice(index)
24
+ .reverse()
25
+ .map(one => one.name)
26
+ .join(' › ')
27
+ : domain.name;
28
+ groups.push({ ownerId: domain.id, inherited: index > 0, path, agents });
29
+ }
30
+ return groups;
31
+ }
32
+ /** The whole save for one action on the card. */
33
+ export function saveInput(settings, change) {
34
+ let agents = settings.agents
35
+ .filter(one => !one.inherited)
36
+ .map(one => ({
37
+ id: one.id,
38
+ name: one.name,
39
+ provider: one.provider,
40
+ model: one.model,
41
+ ...(one.baseURL && { baseURL: one.baseURL }),
42
+ ...(one.organization && { organization: one.organization }),
43
+ active: one.active
44
+ }));
45
+ /* Only this domain's own choice is written - an inherited or a first-by-name default is not pinned here. */
46
+ let defaultAgentId = settings.defaultFrom === 'own' ? settings.defaultAgentId ?? undefined : undefined;
47
+ let turnedOff = settings.agents.filter(one => one.inherited && one.turnedOff).map(one => one.id);
48
+ let inheritedKeys;
49
+ switch (change.kind) {
50
+ case 'default':
51
+ defaultAgentId = change.agentId;
52
+ break;
53
+ case 'turn-off':
54
+ turnedOff = change.off ? [...new Set([...turnedOff, change.agentId])] : turnedOff.filter(id => id !== change.agentId);
55
+ if (change.off && defaultAgentId === change.agentId)
56
+ defaultAgentId = undefined;
57
+ break;
58
+ case 'active':
59
+ agents = agents.map(one => (one.id === change.agentId ? { ...one, active: change.active } : one));
60
+ break;
61
+ case 'delete':
62
+ agents = agents.filter(one => one.id !== change.agentId);
63
+ if (defaultAgentId === change.agentId)
64
+ defaultAgentId = undefined;
65
+ break;
66
+ case 'upsert': {
67
+ const { draft } = change;
68
+ const written = {
69
+ ...(draft.id && { id: draft.id }),
70
+ name: draft.name.trim(),
71
+ provider: draft.provider,
72
+ model: draft.model,
73
+ ...(draft.baseURL.trim() && { baseURL: draft.baseURL.trim() }),
74
+ ...(draft.provider === 'openai' && draft.organization.trim() && { organization: draft.organization.trim() }),
75
+ active: draft.active,
76
+ ...(draft.apiKey.trim() && { apiKey: draft.apiKey.trim() })
77
+ };
78
+ agents = draft.id ? agents.map(one => (one.id === draft.id ? written : one)) : [...agents, written];
79
+ break;
80
+ }
81
+ case 'key-override':
82
+ inheritedKeys = [{ agentId: change.agentId, apiKey: change.apiKey }];
83
+ break;
84
+ case 'key-clear':
85
+ inheritedKeys = [{ agentId: change.agentId, clear: true }];
86
+ break;
87
+ }
88
+ return {
89
+ agents,
90
+ ...(defaultAgentId && { defaultAgentId }),
91
+ turnedOff,
92
+ ...(inheritedKeys && { inheritedKeys })
93
+ };
94
+ }
95
+ //# sourceMappingURL=agent-settings-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-settings-model.js","sourceRoot":"","sources":["../../client/settings/agent-settings-model.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What the AI agent card shows and sends, apart from how it draws (design/ai/agent-settings.md §7).\n *\n * Every action on the card - pick the default, turn an agent off, edit one, delete one, answer an inherited agent with\n * this domain's key - is saved as the domain's whole agent list, so the server checks it as one write.\n */\nexport interface AgentDomain {\n id: string\n name: string\n}\n\nexport interface AgentView {\n id: string\n name: string\n provider: string\n model: string\n baseURL?: string | null\n organization?: string | null\n active: boolean\n owner: AgentDomain\n inherited: boolean\n turnedOff: boolean\n keyState: 'own' | 'inherited' | 'overridden' | 'missing'\n keyDomain?: AgentDomain | null\n keyHint?: string | null\n usable: boolean\n isDefault: boolean\n usedBelow: AgentDomain[]\n}\n\nexport interface AgentSettings {\n chain: AgentDomain[]\n agents: AgentView[]\n defaultAgentId?: string | null\n defaultFrom: 'own' | 'inherited' | 'first' | 'none'\n state: string\n reason?: string | null\n}\n\nexport interface AgentDraft {\n id?: string\n name: string\n provider: string\n model: string\n baseURL: string\n organization: string\n active: boolean\n /** A new key. Empty keeps the stored one. */\n apiKey: string\n}\n\nexport type SaveChange =\n | { kind: 'default'; agentId: string }\n | { kind: 'turn-off'; agentId: string; off: boolean }\n | { kind: 'active'; agentId: string; active: boolean }\n | { kind: 'delete'; agentId: string }\n | { kind: 'upsert'; draft: AgentDraft }\n | { kind: 'key-override'; agentId: string; apiKey: string }\n | { kind: 'key-clear'; agentId: string }\n\nexport function draftOf(agent?: AgentView): AgentDraft {\n return {\n ...(agent && { id: agent.id }),\n name: agent?.name ?? '',\n provider: agent?.provider ?? 'openai',\n model: agent?.model ?? '',\n baseURL: agent?.baseURL ?? '',\n organization: agent?.organization ?? '',\n active: agent?.active ?? true,\n apiKey: ''\n }\n}\n\nexport interface AgentGroup {\n ownerId: string\n inherited: boolean\n /** The owner's place: `그룹 › 본사` when there is more than one domain above, else its name. */\n path: string\n agents: AgentView[]\n}\n\n/** This domain's own agents first, then each domain above, closest first. A domain with no agents has no group. */\nexport function groupByOwner(settings: AgentSettings): AgentGroup[] {\n const groups: AgentGroup[] = []\n const above = settings.chain.length - 1\n for (const [index, domain] of settings.chain.entries()) {\n const agents = settings.agents.filter(one => one.owner.id === domain.id)\n if (!agents.length && index > 0) continue\n const path =\n above > 1\n ? settings.chain\n .slice(index)\n .reverse()\n .map(one => one.name)\n .join(' › ')\n : domain.name\n groups.push({ ownerId: domain.id, inherited: index > 0, path, agents })\n }\n return groups\n}\n\n/** The whole save for one action on the card. */\nexport function saveInput(settings: AgentSettings, change: SaveChange) {\n let agents = settings.agents\n .filter(one => !one.inherited)\n .map(one => ({\n id: one.id,\n name: one.name,\n provider: one.provider,\n model: one.model,\n ...(one.baseURL && { baseURL: one.baseURL }),\n ...(one.organization && { organization: one.organization }),\n active: one.active\n })) as Array<Record<string, any>>\n /* Only this domain's own choice is written - an inherited or a first-by-name default is not pinned here. */\n let defaultAgentId = settings.defaultFrom === 'own' ? settings.defaultAgentId ?? undefined : undefined\n let turnedOff = settings.agents.filter(one => one.inherited && one.turnedOff).map(one => one.id)\n let inheritedKeys: Array<{ agentId: string; apiKey?: string; clear?: boolean }> | undefined\n\n switch (change.kind) {\n case 'default':\n defaultAgentId = change.agentId\n break\n case 'turn-off':\n turnedOff = change.off ? [...new Set([...turnedOff, change.agentId])] : turnedOff.filter(id => id !== change.agentId)\n if (change.off && defaultAgentId === change.agentId) defaultAgentId = undefined\n break\n case 'active':\n agents = agents.map(one => (one.id === change.agentId ? { ...one, active: change.active } : one))\n break\n case 'delete':\n agents = agents.filter(one => one.id !== change.agentId)\n if (defaultAgentId === change.agentId) defaultAgentId = undefined\n break\n case 'upsert': {\n const { draft } = change\n const written = {\n ...(draft.id && { id: draft.id }),\n name: draft.name.trim(),\n provider: draft.provider,\n model: draft.model,\n ...(draft.baseURL.trim() && { baseURL: draft.baseURL.trim() }),\n ...(draft.provider === 'openai' && draft.organization.trim() && { organization: draft.organization.trim() }),\n active: draft.active,\n ...(draft.apiKey.trim() && { apiKey: draft.apiKey.trim() })\n }\n agents = draft.id ? agents.map(one => (one.id === draft.id ? written : one)) : [...agents, written]\n break\n }\n case 'key-override':\n inheritedKeys = [{ agentId: change.agentId, apiKey: change.apiKey }]\n break\n case 'key-clear':\n inheritedKeys = [{ agentId: change.agentId, clear: true }]\n break\n }\n\n return {\n agents,\n ...(defaultAgentId && { defaultAgentId }),\n turnedOff,\n ...(inheritedKeys && { inheritedKeys })\n }\n}\n"]}