@eclipse-docks/extension-ai-system 0.7.140 → 0.7.141

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 (211) hide show
  1. package/README.md +72 -0
  2. package/dist/api.d.ts +1 -7
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +2 -2
  5. package/dist/cognitive-extension-CG_d39K5.js +2129 -0
  6. package/dist/cognitive-extension-CG_d39K5.js.map +1 -0
  7. package/dist/cognitive-extension.d.ts +1 -0
  8. package/dist/cognitive-extension.d.ts.map +1 -0
  9. package/dist/i18n.de-BrLTiN56.js +11 -0
  10. package/dist/i18n.de-BrLTiN56.js.map +1 -0
  11. package/dist/i18n.en-MkwuLlOD.js +11 -0
  12. package/dist/i18n.en-MkwuLlOD.js.map +1 -0
  13. package/dist/index.js +8 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/runtime/attachments.d.ts +89 -0
  16. package/dist/runtime/attachments.d.ts.map +1 -0
  17. package/dist/runtime/cognitive-runtime.d.ts +83 -0
  18. package/dist/runtime/cognitive-runtime.d.ts.map +1 -0
  19. package/dist/runtime/compact/compaction.d.ts +29 -0
  20. package/dist/runtime/compact/compaction.d.ts.map +1 -0
  21. package/dist/runtime/constants.d.ts +9 -0
  22. package/dist/runtime/constants.d.ts.map +1 -0
  23. package/dist/runtime/context/usage.d.ts +35 -0
  24. package/dist/runtime/context/usage.d.ts.map +1 -0
  25. package/dist/runtime/core/index.d.ts +4 -0
  26. package/dist/runtime/core/index.d.ts.map +1 -0
  27. package/dist/runtime/core/needs.d.ts +5 -0
  28. package/dist/runtime/core/needs.d.ts.map +1 -0
  29. package/dist/runtime/core/plutchik.d.ts +8 -0
  30. package/dist/runtime/core/plutchik.d.ts.map +1 -0
  31. package/dist/runtime/core/tick.d.ts +23 -0
  32. package/dist/runtime/core/tick.d.ts.map +1 -0
  33. package/dist/runtime/events.d.ts +21 -0
  34. package/dist/runtime/events.d.ts.map +1 -0
  35. package/dist/runtime/index.d.ts +14 -0
  36. package/dist/runtime/index.d.ts.map +1 -0
  37. package/dist/runtime/memory/index.d.ts +2 -0
  38. package/dist/runtime/memory/index.d.ts.map +1 -0
  39. package/dist/runtime/memory/store.d.ts +27 -0
  40. package/dist/runtime/memory/store.d.ts.map +1 -0
  41. package/dist/runtime/models/effort-support.d.ts +31 -0
  42. package/dist/runtime/models/effort-support.d.ts.map +1 -0
  43. package/dist/runtime/models/index.d.ts +7 -0
  44. package/dist/runtime/models/index.d.ts.map +1 -0
  45. package/dist/runtime/models/openai-compatible.d.ts +39 -0
  46. package/dist/runtime/models/openai-compatible.d.ts.map +1 -0
  47. package/dist/runtime/models/provider-config.d.ts +23 -0
  48. package/dist/runtime/models/provider-config.d.ts.map +1 -0
  49. package/dist/runtime/models/provider-registry.d.ts +16 -0
  50. package/dist/runtime/models/provider-registry.d.ts.map +1 -0
  51. package/dist/runtime/models/router.d.ts +12 -0
  52. package/dist/runtime/models/router.d.ts.map +1 -0
  53. package/dist/runtime/models/types.d.ts +88 -0
  54. package/dist/runtime/models/types.d.ts.map +1 -0
  55. package/dist/runtime/session/index.d.ts +2 -0
  56. package/dist/runtime/session/index.d.ts.map +1 -0
  57. package/dist/runtime/session/session-runtime.d.ts +48 -0
  58. package/dist/runtime/session/session-runtime.d.ts.map +1 -0
  59. package/dist/runtime/specialists/classify-risk.d.ts +15 -0
  60. package/dist/runtime/specialists/classify-risk.d.ts.map +1 -0
  61. package/dist/runtime/specialists/index.d.ts +12 -0
  62. package/dist/runtime/specialists/index.d.ts.map +1 -0
  63. package/dist/runtime/specialists/loop.d.ts +25 -0
  64. package/dist/runtime/specialists/loop.d.ts.map +1 -0
  65. package/dist/runtime/tools/attachment-adapter.d.ts +6 -0
  66. package/dist/runtime/tools/attachment-adapter.d.ts.map +1 -0
  67. package/dist/runtime/tools/broker.d.ts +52 -0
  68. package/dist/runtime/tools/broker.d.ts.map +1 -0
  69. package/dist/runtime/tools/command-adapter.d.ts +5 -0
  70. package/dist/runtime/tools/command-adapter.d.ts.map +1 -0
  71. package/dist/runtime/tools/index.d.ts +5 -0
  72. package/dist/runtime/tools/index.d.ts.map +1 -0
  73. package/dist/runtime/tools/workspace-adapter.d.ts +4 -0
  74. package/dist/runtime/tools/workspace-adapter.d.ts.map +1 -0
  75. package/dist/runtime/types.d.ts +145 -0
  76. package/dist/runtime/types.d.ts.map +1 -0
  77. package/dist/runtime-DfNGzEjU.js +2952 -0
  78. package/dist/runtime-DfNGzEjU.js.map +1 -0
  79. package/dist/view/cognitive-settings-editor.d.ts +40 -0
  80. package/dist/view/cognitive-settings-editor.d.ts.map +1 -0
  81. package/dist/view/cognitive-view.d.ts +83 -0
  82. package/dist/view/cognitive-view.d.ts.map +1 -0
  83. package/package.json +15 -11
  84. package/dist/agents/agent-registry.d.ts +0 -8
  85. package/dist/agents/agent-registry.d.ts.map +0 -1
  86. package/dist/agents/index.d.ts +0 -6
  87. package/dist/agents/index.d.ts.map +0 -1
  88. package/dist/agents/message-processor.d.ts +0 -9
  89. package/dist/agents/message-processor.d.ts.map +0 -1
  90. package/dist/agents/orchestrator.d.ts +0 -12
  91. package/dist/agents/orchestrator.d.ts.map +0 -1
  92. package/dist/agents/prompt-builder.d.ts +0 -21
  93. package/dist/agents/prompt-builder.d.ts.map +0 -1
  94. package/dist/agents/reviewer.d.ts +0 -15
  95. package/dist/agents/reviewer.d.ts.map +0 -1
  96. package/dist/ai-service-Cmafte5S.js +0 -1866
  97. package/dist/ai-service-Cmafte5S.js.map +0 -1
  98. package/dist/ai-system-extension-CsfSiwNJ.js +0 -2324
  99. package/dist/ai-system-extension-CsfSiwNJ.js.map +0 -1
  100. package/dist/ai-system-extension.d.ts +0 -1
  101. package/dist/ai-system-extension.d.ts.map +0 -1
  102. package/dist/chat-provider-contributions.d.ts +0 -2
  103. package/dist/chat-provider-contributions.d.ts.map +0 -1
  104. package/dist/core/constants.d.ts +0 -21
  105. package/dist/core/constants.d.ts.map +0 -1
  106. package/dist/core/index.d.ts +0 -4
  107. package/dist/core/index.d.ts.map +0 -1
  108. package/dist/core/interfaces.d.ts +0 -138
  109. package/dist/core/interfaces.d.ts.map +0 -1
  110. package/dist/core/message-utils.d.ts +0 -4
  111. package/dist/core/message-utils.d.ts.map +0 -1
  112. package/dist/core/types.d.ts +0 -128
  113. package/dist/core/types.d.ts.map +0 -1
  114. package/dist/prompt-enhancer-contributions.d.ts +0 -2
  115. package/dist/prompt-enhancer-contributions.d.ts.map +0 -1
  116. package/dist/providers/base-provider.d.ts +0 -15
  117. package/dist/providers/base-provider.d.ts.map +0 -1
  118. package/dist/providers/index.d.ts +0 -9
  119. package/dist/providers/index.d.ts.map +0 -1
  120. package/dist/providers/ollama-provider.d.ts +0 -7
  121. package/dist/providers/ollama-provider.d.ts.map +0 -1
  122. package/dist/providers/openai-provider.d.ts +0 -7
  123. package/dist/providers/openai-provider.d.ts.map +0 -1
  124. package/dist/providers/provider-factory.d.ts +0 -10
  125. package/dist/providers/provider-factory.d.ts.map +0 -1
  126. package/dist/providers/provider-utils.d.ts +0 -5
  127. package/dist/providers/provider-utils.d.ts.map +0 -1
  128. package/dist/providers/streaming/ollama-parser.d.ts +0 -9
  129. package/dist/providers/streaming/ollama-parser.d.ts.map +0 -1
  130. package/dist/providers/streaming/sse-parser.d.ts +0 -10
  131. package/dist/providers/streaming/sse-parser.d.ts.map +0 -1
  132. package/dist/providers/streaming/stream-parser.d.ts +0 -10
  133. package/dist/providers/streaming/stream-parser.d.ts.map +0 -1
  134. package/dist/service/ai-service.d.ts +0 -47
  135. package/dist/service/ai-service.d.ts.map +0 -1
  136. package/dist/service/token-usage-tracker.d.ts +0 -16
  137. package/dist/service/token-usage-tracker.d.ts.map +0 -1
  138. package/dist/task/task-checkpoint.d.ts +0 -12
  139. package/dist/task/task-checkpoint.d.ts.map +0 -1
  140. package/dist/task/task-plan.d.ts +0 -7
  141. package/dist/task/task-plan.d.ts.map +0 -1
  142. package/dist/task/task-runner.d.ts +0 -10
  143. package/dist/task/task-runner.d.ts.map +0 -1
  144. package/dist/tools/index.d.ts +0 -6
  145. package/dist/tools/index.d.ts.map +0 -1
  146. package/dist/tools/token-estimator.d.ts +0 -12
  147. package/dist/tools/token-estimator.d.ts.map +0 -1
  148. package/dist/tools/tool-call-accumulator.d.ts +0 -9
  149. package/dist/tools/tool-call-accumulator.d.ts.map +0 -1
  150. package/dist/tools/tool-executor.d.ts +0 -13
  151. package/dist/tools/tool-executor.d.ts.map +0 -1
  152. package/dist/tools/tool-name-utils.d.ts +0 -2
  153. package/dist/tools/tool-name-utils.d.ts.map +0 -1
  154. package/dist/tools/tool-registry.d.ts +0 -7
  155. package/dist/tools/tool-registry.d.ts.map +0 -1
  156. package/dist/utils/token-estimator.d.ts +0 -2
  157. package/dist/utils/token-estimator.d.ts.map +0 -1
  158. package/dist/utils/tool-detector.d.ts +0 -10
  159. package/dist/utils/tool-detector.d.ts.map +0 -1
  160. package/dist/view/agent-group-manager.d.ts +0 -37
  161. package/dist/view/agent-group-manager.d.ts.map +0 -1
  162. package/dist/view/aiview.d.ts +0 -42
  163. package/dist/view/aiview.d.ts.map +0 -1
  164. package/dist/view/components/ai-agent-response-group.d.ts +0 -18
  165. package/dist/view/components/ai-agent-response-group.d.ts.map +0 -1
  166. package/dist/view/components/ai-chat-input.d.ts +0 -20
  167. package/dist/view/components/ai-chat-input.d.ts.map +0 -1
  168. package/dist/view/components/ai-chat-message.d.ts +0 -22
  169. package/dist/view/components/ai-chat-message.d.ts.map +0 -1
  170. package/dist/view/components/ai-config-editor.d.ts +0 -33
  171. package/dist/view/components/ai-config-editor.d.ts.map +0 -1
  172. package/dist/view/components/ai-empty-state.d.ts +0 -13
  173. package/dist/view/components/ai-empty-state.d.ts.map +0 -1
  174. package/dist/view/components/ai-tool-approval.d.ts +0 -26
  175. package/dist/view/components/ai-tool-approval.d.ts.map +0 -1
  176. package/dist/view/components/index.d.ts +0 -7
  177. package/dist/view/components/index.d.ts.map +0 -1
  178. package/dist/view/provider-manager.d.ts +0 -30
  179. package/dist/view/provider-manager.d.ts.map +0 -1
  180. package/dist/view/session-manager.d.ts +0 -25
  181. package/dist/view/session-manager.d.ts.map +0 -1
  182. package/dist/view/stream-manager.d.ts +0 -23
  183. package/dist/view/stream-manager.d.ts.map +0 -1
  184. package/dist/view/task-progress-panel.d.ts +0 -14
  185. package/dist/view/task-progress-panel.d.ts.map +0 -1
  186. package/dist/view/token-usage.d.ts +0 -18
  187. package/dist/view/token-usage.d.ts.map +0 -1
  188. package/dist/view/workspace-panel.d.ts +0 -15
  189. package/dist/view/workspace-panel.d.ts.map +0 -1
  190. package/dist/workflows/base-sequential-workflow.d.ts +0 -13
  191. package/dist/workflows/base-sequential-workflow.d.ts.map +0 -1
  192. package/dist/workflows/conditional-workflow.d.ts +0 -7
  193. package/dist/workflows/conditional-workflow.d.ts.map +0 -1
  194. package/dist/workflows/index.d.ts +0 -10
  195. package/dist/workflows/index.d.ts.map +0 -1
  196. package/dist/workflows/orchestrated-workflow.d.ts +0 -10
  197. package/dist/workflows/orchestrated-workflow.d.ts.map +0 -1
  198. package/dist/workflows/parallel-workflow.d.ts +0 -6
  199. package/dist/workflows/parallel-workflow.d.ts.map +0 -1
  200. package/dist/workflows/pipeline-workflow.d.ts +0 -7
  201. package/dist/workflows/pipeline-workflow.d.ts.map +0 -1
  202. package/dist/workflows/review-workflow.d.ts +0 -10
  203. package/dist/workflows/review-workflow.d.ts.map +0 -1
  204. package/dist/workflows/sequential-workflow.d.ts +0 -7
  205. package/dist/workflows/sequential-workflow.d.ts.map +0 -1
  206. package/dist/workflows/workflow-engine.d.ts +0 -8
  207. package/dist/workflows/workflow-engine.d.ts.map +0 -1
  208. package/dist/workflows/workflow-strategy.d.ts +0 -8
  209. package/dist/workflows/workflow-strategy.d.ts.map +0 -1
  210. package/dist/workspace/workspace.d.ts +0 -20
  211. package/dist/workspace/workspace.d.ts.map +0 -1
@@ -0,0 +1,2129 @@
1
+ import { _ as AI_SETTINGS_KEY, a as formatContextPercent, b as TOPIC, c as readPromptAttachmentFromFile, d as defaultProvidersSettings, g as AI_SETTINGS_EDITOR_ID, i as formatBucketDetail, m as detectEffortCapability, n as cognitiveRuntime, o as formatTokenCount, p as parseModelIds, r as estimateSessionContextUsage, s as ATTACHMENT_FILE_ACCEPT, u as fetchOpenAICompatibleModels, v as AI_VIEW_NAME, y as OPEN_AI_SETTINGS_COMMAND } from "./runtime-DfNGzEjU.js";
2
+ import { DocksPart, SIDEBAR_AUXILIARY, confirmDialog, contributionRegistry, editorRegistry, registerAll, rootContext, toastError, toastInfo } from "@eclipse-docks/core";
3
+ import { css, customElement, html, nothing, property, query, state } from "@eclipse-docks/core/externals/lit";
4
+ import "@eclipse-docks/core/externals/webawesome";
5
+ import _decorate from "@oxc-project/runtime/helpers/decorate";
6
+ //#region src/view/cognitive-view.ts
7
+ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
8
+ constructor(..._args) {
9
+ super(..._args);
10
+ this.scrollMode = "none";
11
+ this.sessions = [];
12
+ this.focusedId = null;
13
+ this.draft = "";
14
+ this.pendingAttachmentIds = [];
15
+ this.dragOver = false;
16
+ this.previewAttachmentId = null;
17
+ this.busy = false;
18
+ this.pendingPermission = null;
19
+ this.policyMode = "auto";
20
+ this.optimizeFor = "balance";
21
+ this.manualModel = "";
22
+ this.manualEffort = "auto";
23
+ this.approvalMode = "auto_safe";
24
+ this.modelIds = [];
25
+ this.streamingParts = [];
26
+ this.historyOpen = false;
27
+ this.historyQuery = "";
28
+ this.unsubs = [];
29
+ }
30
+ static {
31
+ this.styles = css`
32
+ :host {
33
+ height: 100%;
34
+ min-height: 0;
35
+ background: var(--wa-color-surface-default);
36
+ color: var(--wa-color-text-normal);
37
+ font-family: var(--wa-font-family-body);
38
+ }
39
+
40
+ .part-content,
41
+ .part-content-inner {
42
+ height: 100%;
43
+ min-height: 0;
44
+ }
45
+
46
+ .cognitive-shell {
47
+ display: flex;
48
+ flex-direction: column;
49
+ height: 100%;
50
+ min-height: 0;
51
+ overflow: hidden;
52
+ /* Establish a definite containing block for wa-scroller's height: 100%. */
53
+ position: relative;
54
+ }
55
+
56
+ wa-dropdown.history-menu::part(menu) {
57
+ min-width: 18rem;
58
+ max-width: min(24rem, 92vw);
59
+ max-height: min(28rem, 70vh);
60
+ overflow-y: auto;
61
+ padding-top: 0;
62
+ }
63
+
64
+ .history-search {
65
+ position: sticky;
66
+ top: 0;
67
+ z-index: 1;
68
+ display: block;
69
+ padding: var(--wa-space-s);
70
+ background: var(--wa-color-surface-raised);
71
+ border-bottom: var(--wa-border-width-s) solid var(--wa-color-border-normal);
72
+ }
73
+
74
+ .history-search wa-input {
75
+ width: 100%;
76
+ }
77
+
78
+ .history-item-title {
79
+ flex: 1;
80
+ min-width: 0;
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
83
+ white-space: nowrap;
84
+ }
85
+
86
+ .history-item-meta {
87
+ color: var(--wa-color-text-quiet);
88
+ font-size: var(--wa-font-size-xs);
89
+ flex-shrink: 0;
90
+ }
91
+
92
+ .history-empty {
93
+ padding: var(--wa-space-m);
94
+ color: var(--wa-color-text-quiet);
95
+ font-size: var(--wa-font-size-s);
96
+ text-align: center;
97
+ }
98
+
99
+ wa-scroller.transcript {
100
+ /* flex-basis 0 so the host gets a definite height; wa-scroller vertical uses height:100%. */
101
+ flex: 1 1 0;
102
+ min-height: 0;
103
+ height: 100%;
104
+ overflow: hidden;
105
+ }
106
+
107
+ .transcript-inner {
108
+ display: flex;
109
+ flex-direction: column;
110
+ gap: var(--wa-space-m);
111
+ padding: var(--wa-space-m);
112
+ min-height: 100%;
113
+ box-sizing: border-box;
114
+ }
115
+
116
+ /* Pushes short threads to the bottom without breaking overflow scroll. */
117
+ .transcript-spacer {
118
+ flex: 1 0 0;
119
+ min-height: 0;
120
+ }
121
+
122
+ .msg {
123
+ position: relative;
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: var(--wa-space-2xs);
127
+ max-width: 100%;
128
+ }
129
+
130
+ .msg.user {
131
+ align-items: flex-end;
132
+ }
133
+
134
+ .msg-meta {
135
+ position: absolute;
136
+ right: var(--wa-space-2xs);
137
+ bottom: var(--wa-space-2xs);
138
+ z-index: 1;
139
+ display: flex;
140
+ gap: var(--wa-space-2xs);
141
+ align-items: center;
142
+ flex-wrap: wrap;
143
+ justify-content: flex-end;
144
+ max-width: calc(100% - var(--wa-space-s));
145
+ padding: var(--wa-space-3xs) var(--wa-space-2xs);
146
+ border-radius: var(--wa-border-radius-m);
147
+ background: color-mix(in srgb, var(--wa-color-surface-default) 88%, transparent);
148
+ font-size: var(--wa-font-size-xs);
149
+ color: var(--wa-color-text-quiet);
150
+ opacity: 0;
151
+ pointer-events: none;
152
+ transition: opacity var(--wa-transition-fast, 120ms) ease;
153
+ }
154
+
155
+ .msg:hover .msg-meta,
156
+ .msg:focus-within .msg-meta {
157
+ opacity: 1;
158
+ pointer-events: auto;
159
+ }
160
+
161
+ .bubble {
162
+ padding: var(--wa-space-s) var(--wa-space-m);
163
+ border-radius: var(--wa-border-radius-l);
164
+ background: var(--wa-color-surface-raised);
165
+ border: var(--wa-border-width-s) solid var(--wa-color-border-normal);
166
+ white-space: pre-wrap;
167
+ line-height: var(--wa-line-height-normal);
168
+ max-width: 100%;
169
+ }
170
+
171
+ .msg.user .bubble {
172
+ background: color-mix(in srgb, var(--wa-color-brand-fill-normal) 22%, transparent);
173
+ border-color: color-mix(in srgb, var(--wa-color-brand-fill-normal) 40%, transparent);
174
+ }
175
+
176
+ .streaming-meta {
177
+ display: flex;
178
+ gap: var(--wa-space-2xs);
179
+ align-items: center;
180
+ color: var(--wa-color-text-quiet);
181
+ font-size: var(--wa-font-size-s);
182
+ }
183
+
184
+ .composer-shell {
185
+ display: flex;
186
+ flex-direction: column;
187
+ gap: var(--wa-space-s);
188
+ padding: var(--wa-space-s) var(--wa-space-m) var(--wa-space-m);
189
+ border-top: var(--wa-border-width-s) solid var(--wa-color-border-normal);
190
+ flex-shrink: 0;
191
+ }
192
+
193
+ wa-dropdown.settings-menu::part(menu) {
194
+ min-width: 16rem;
195
+ max-width: min(28rem, 92vw);
196
+ }
197
+
198
+ wa-dropdown.context-menu::part(menu) {
199
+ min-width: 17rem;
200
+ max-width: min(22rem, 92vw);
201
+ padding: var(--wa-space-s);
202
+ }
203
+
204
+ .context-ring {
205
+ --size: 1.35rem;
206
+ --track-width: 2.5px;
207
+ --indicator-width: 2.5px;
208
+ font-size: 0;
209
+ }
210
+
211
+ .context-panel {
212
+ display: flex;
213
+ flex-direction: column;
214
+ gap: var(--wa-space-s);
215
+ }
216
+
217
+ .context-header {
218
+ display: flex;
219
+ justify-content: space-between;
220
+ align-items: baseline;
221
+ gap: var(--wa-space-s);
222
+ }
223
+
224
+ .context-header strong {
225
+ font-size: var(--wa-font-size-s);
226
+ }
227
+
228
+ .context-header span {
229
+ color: var(--wa-color-text-quiet);
230
+ font-size: var(--wa-font-size-xs);
231
+ }
232
+
233
+ .context-bars {
234
+ display: flex;
235
+ flex-direction: column;
236
+ gap: var(--wa-space-2xs);
237
+ }
238
+
239
+ .context-row {
240
+ display: grid;
241
+ grid-template-columns: 1fr auto;
242
+ gap: var(--wa-space-2xs);
243
+ font-size: var(--wa-font-size-xs);
244
+ }
245
+
246
+ .context-row-label {
247
+ color: var(--wa-color-text-quiet);
248
+ }
249
+
250
+ .context-meter {
251
+ grid-column: 1 / -1;
252
+ height: 0.35rem;
253
+ border-radius: var(--wa-border-radius-pill);
254
+ background: var(--wa-color-neutral-fill-quiet);
255
+ overflow: hidden;
256
+ }
257
+
258
+ .context-meter > span {
259
+ display: block;
260
+ height: 100%;
261
+ border-radius: inherit;
262
+ background: var(--bucket-color, var(--wa-color-brand-fill-normal));
263
+ }
264
+
265
+ .context-footer {
266
+ display: flex;
267
+ flex-direction: column;
268
+ gap: var(--wa-space-2xs);
269
+ }
270
+
271
+ .context-remaining {
272
+ color: var(--wa-color-text-quiet);
273
+ font-size: var(--wa-font-size-xs);
274
+ }
275
+
276
+ .composer {
277
+ display: flex;
278
+ flex-direction: column;
279
+ gap: var(--wa-space-2xs);
280
+ flex: 1;
281
+ min-width: 0;
282
+ }
283
+
284
+ .composer-row {
285
+ display: flex;
286
+ gap: var(--wa-space-s);
287
+ align-items: flex-end;
288
+ }
289
+
290
+ .composer wa-textarea {
291
+ flex: 1;
292
+ min-width: 0;
293
+ }
294
+
295
+ .composer-actions {
296
+ display: flex;
297
+ align-items: center;
298
+ gap: var(--wa-space-2xs);
299
+ flex-shrink: 0;
300
+ }
301
+
302
+ .composer-shell.drag-over {
303
+ outline: 2px dashed var(--wa-color-brand-border-normal);
304
+ outline-offset: -2px;
305
+ background: color-mix(in srgb, var(--wa-color-brand-fill-quiet) 35%, transparent);
306
+ }
307
+
308
+ wa-dropdown.attach-menu::part(menu) {
309
+ min-width: 18rem;
310
+ max-width: min(24rem, 92vw);
311
+ padding: var(--wa-space-2xs);
312
+ }
313
+
314
+ .attach-panel {
315
+ display: flex;
316
+ flex-direction: column;
317
+ gap: var(--wa-space-2xs);
318
+ max-height: min(22rem, 50vh);
319
+ }
320
+
321
+ .attach-list {
322
+ display: flex;
323
+ flex-direction: column;
324
+ gap: var(--wa-space-2xs);
325
+ overflow: auto;
326
+ min-height: 0;
327
+ }
328
+
329
+ .attach-row {
330
+ display: flex;
331
+ align-items: center;
332
+ gap: var(--wa-space-2xs);
333
+ min-width: 0;
334
+ }
335
+
336
+ .attach-preview-btn {
337
+ display: flex;
338
+ align-items: center;
339
+ gap: var(--wa-space-s);
340
+ flex: 1;
341
+ min-width: 0;
342
+ margin: 0;
343
+ padding: var(--wa-space-2xs);
344
+ border: none;
345
+ border-radius: var(--wa-border-radius-s);
346
+ background: transparent;
347
+ color: inherit;
348
+ font: inherit;
349
+ text-align: start;
350
+ cursor: pointer;
351
+ }
352
+
353
+ .attach-preview-btn:hover {
354
+ background: var(--wa-color-neutral-fill-quiet);
355
+ }
356
+
357
+ .attach-thumb,
358
+ .msg-file-thumb {
359
+ width: 2.5rem;
360
+ height: 2.5rem;
361
+ object-fit: cover;
362
+ border-radius: var(--wa-border-radius-s);
363
+ border: var(--wa-border-width-s) solid var(--wa-color-border-normal);
364
+ flex-shrink: 0;
365
+ }
366
+
367
+ .attach-file-icon {
368
+ display: grid;
369
+ place-items: center;
370
+ width: 2.5rem;
371
+ height: 2.5rem;
372
+ border-radius: var(--wa-border-radius-s);
373
+ border: var(--wa-border-width-s) solid var(--wa-color-border-normal);
374
+ color: var(--wa-color-text-quiet);
375
+ flex-shrink: 0;
376
+ }
377
+
378
+ .attach-name {
379
+ flex: 1;
380
+ min-width: 0;
381
+ overflow: hidden;
382
+ text-overflow: ellipsis;
383
+ white-space: nowrap;
384
+ font-size: var(--wa-font-size-s);
385
+ }
386
+
387
+ .attach-empty {
388
+ color: var(--wa-color-text-quiet);
389
+ font-size: var(--wa-font-size-xs);
390
+ padding: var(--wa-space-s);
391
+ }
392
+
393
+ .msg-file-thumb {
394
+ width: 4.5rem;
395
+ height: 4.5rem;
396
+ }
397
+
398
+ .attach-dialog-body {
399
+ display: flex;
400
+ flex-direction: column;
401
+ gap: var(--wa-space-s);
402
+ max-height: min(70vh, 40rem);
403
+ overflow: auto;
404
+ }
405
+
406
+ .attach-dialog-image {
407
+ max-width: 100%;
408
+ height: auto;
409
+ border-radius: var(--wa-border-radius-m);
410
+ }
411
+
412
+ .attach-dialog-text {
413
+ margin: 0;
414
+ white-space: pre-wrap;
415
+ word-break: break-word;
416
+ font-family: var(--wa-font-family-code, ui-monospace, monospace);
417
+ font-size: var(--wa-font-size-s);
418
+ }
419
+
420
+ .msg-files {
421
+ display: flex;
422
+ flex-wrap: wrap;
423
+ gap: var(--wa-space-2xs);
424
+ margin-bottom: var(--wa-space-2xs);
425
+ }
426
+
427
+ .approval-actions {
428
+ display: flex;
429
+ gap: var(--wa-space-s);
430
+ margin-top: var(--wa-space-s);
431
+ flex-wrap: wrap;
432
+ }
433
+
434
+ pre.args {
435
+ margin: var(--wa-space-s) 0 0;
436
+ font-family: var(--wa-font-family-code, var(--wa-font-mono));
437
+ font-size: var(--wa-font-size-xs);
438
+ white-space: pre-wrap;
439
+ overflow-x: auto;
440
+ }
441
+
442
+ wa-details {
443
+ font-size: var(--wa-font-size-s);
444
+ }
445
+
446
+ wa-details.msg-details {
447
+ width: 100%;
448
+ font-size: var(--wa-font-size-s);
449
+ }
450
+
451
+ wa-details.msg-details::part(base),
452
+ wa-details.msg-details::part(details) {
453
+ border: none;
454
+ background: transparent;
455
+ }
456
+
457
+ wa-details.msg-details::part(header) {
458
+ align-items: flex-start;
459
+ gap: var(--wa-space-2xs);
460
+ padding: 0;
461
+ }
462
+
463
+ wa-details.msg-details::part(summary) {
464
+ flex: 1;
465
+ min-width: 0;
466
+ }
467
+
468
+ wa-details.msg-details::part(content) {
469
+ padding: var(--wa-space-s) 0 0;
470
+ }
471
+
472
+ .msg-summary {
473
+ display: flex;
474
+ flex-direction: column;
475
+ gap: var(--wa-space-2xs);
476
+ width: 100%;
477
+ }
478
+
479
+ .msg-tech {
480
+ display: flex;
481
+ flex-direction: column;
482
+ gap: var(--wa-space-s);
483
+ margin-inline-start: var(--wa-space-l);
484
+ padding-inline-start: var(--wa-space-m);
485
+ border-inline-start: var(--wa-border-width-s) solid var(--wa-color-border-normal);
486
+ }
487
+
488
+ .tool-heading {
489
+ font-size: var(--wa-font-size-xs);
490
+ color: var(--wa-color-text-quiet);
491
+ margin-bottom: var(--wa-space-2xs);
492
+ }
493
+ `;
494
+ }
495
+ doBeforeUI() {
496
+ cognitiveRuntime.start();
497
+ this.syncSessions();
498
+ this.syncModels();
499
+ this.approvalMode = cognitiveRuntime.getApprovalMode();
500
+ cognitiveRuntime.whenReady().then(() => {
501
+ this.syncSessions();
502
+ this.syncModels();
503
+ this.approvalMode = cognitiveRuntime.getApprovalMode();
504
+ this.scheduleScrollToBottom();
505
+ });
506
+ this.unsubs.push(cognitiveRuntime.events.subscribe(TOPIC.sessionChanged, () => {
507
+ this.syncSessions();
508
+ this.scheduleScrollToBottom();
509
+ }), cognitiveRuntime.events.subscribe(TOPIC.providersChanged, () => this.syncModels()), cognitiveRuntime.events.subscribe(TOPIC.approvalModeChanged, (mode) => {
510
+ this.approvalMode = mode;
511
+ }), cognitiveRuntime.events.subscribe(TOPIC.permissionAsked, (payload) => {
512
+ this.pendingPermission = payload;
513
+ this.scheduleScrollToBottom();
514
+ }), cognitiveRuntime.events.subscribe(TOPIC.messagePart, (payload) => {
515
+ const { sessionId, part } = payload;
516
+ if (sessionId !== this.focusedId) return;
517
+ this.streamingParts = mergePart(this.streamingParts, part);
518
+ this.scheduleScrollToBottom();
519
+ }), cognitiveRuntime.events.subscribe(TOPIC.messageDone, () => {
520
+ this.streamingParts = [];
521
+ this.syncSessions();
522
+ this.scheduleScrollToBottom();
523
+ }));
524
+ }
525
+ doClose() {
526
+ if (this.scrollDebounceTimer) clearTimeout(this.scrollDebounceTimer);
527
+ this.scrollDebounceTimer = void 0;
528
+ for (const u of this.unsubs) u();
529
+ this.unsubs = [];
530
+ }
531
+ syncSessions() {
532
+ const byRecency = cognitiveRuntime.sessions.list();
533
+ this.sessions = [...byRecency].sort((a, b) => a.createdAt - b.createdAt);
534
+ const focused = cognitiveRuntime.sessions.focused();
535
+ const activeId = focused?.id ?? byRecency[0]?.id ?? null;
536
+ if (activeId && focused?.id !== activeId) cognitiveRuntime.sessions.focus(activeId);
537
+ this.focusedId = activeId;
538
+ }
539
+ scheduleScrollToBottom() {
540
+ if (this.scrollDebounceTimer) clearTimeout(this.scrollDebounceTimer);
541
+ this.scrollDebounceTimer = setTimeout(() => {
542
+ this.scrollDebounceTimer = void 0;
543
+ this.scrollToBottom();
544
+ }, 50);
545
+ }
546
+ async scrollToBottom() {
547
+ await this.updateComplete;
548
+ await new Promise((resolve) => requestAnimationFrame(() => resolve()));
549
+ const scroller = this.renderRoot.querySelector("wa-scroller.transcript");
550
+ if (!scroller?.shadowRoot) return;
551
+ const container = scroller.shadowRoot.querySelector("#content");
552
+ if (!(container instanceof HTMLElement)) return;
553
+ container.scrollTop = container.scrollHeight;
554
+ }
555
+ focusedSession() {
556
+ return this.sessions.find((s) => s.id === this.focusedId) ?? this.sessions[0];
557
+ }
558
+ deleteSession(sessionId) {
559
+ if (this.sessions.length <= 1) {
560
+ cognitiveRuntime.sessions.createSession({ title: "Chat" });
561
+ cognitiveRuntime.sessions.remove(sessionId);
562
+ } else cognitiveRuntime.sessions.remove(sessionId);
563
+ this.streamingParts = [];
564
+ this.syncSessions();
565
+ cognitiveRuntime.commitSessions();
566
+ }
567
+ openSession(sessionId) {
568
+ if (!cognitiveRuntime.sessions.focus(sessionId)) return;
569
+ this.streamingParts = [];
570
+ this.syncSessions();
571
+ this.historyOpen = false;
572
+ this.historyQuery = "";
573
+ cognitiveRuntime.commitSessions();
574
+ this.scheduleScrollToBottom();
575
+ }
576
+ newSession() {
577
+ cognitiveRuntime.sessions.createSession({ title: "Chat" });
578
+ this.streamingParts = [];
579
+ this.historyOpen = false;
580
+ this.historyQuery = "";
581
+ this.syncSessions();
582
+ cognitiveRuntime.commitSessions();
583
+ }
584
+ historySessions() {
585
+ const q = this.historyQuery.trim().toLowerCase();
586
+ const sorted = [...this.sessions].sort((a, b) => b.updatedAt - a.updatedAt);
587
+ if (!q) return sorted;
588
+ return sorted.filter((s) => {
589
+ if (s.title.toLowerCase().includes(q)) return true;
590
+ return s.history.some((m) => m.parts.some((p) => {
591
+ if (p.type === "text") return p.text.toLowerCase().includes(q);
592
+ if (p.type === "file") return p.name.toLowerCase().includes(q) || p.id.toLowerCase().includes(q) || (p.path?.toLowerCase().includes(q) ?? false);
593
+ return false;
594
+ }));
595
+ });
596
+ }
597
+ syncModels() {
598
+ this.modelIds = cognitiveRuntime.listConfiguredModels();
599
+ if (!this.manualModel || !this.modelIds.includes(this.manualModel)) this.manualModel = this.modelIds[0] ?? cognitiveRuntime.defaultModelId();
600
+ }
601
+ effortChoices() {
602
+ const cap = detectEffortCapability(this.manualModel || cognitiveRuntime.defaultModelId());
603
+ if (!cap.supported) return ["auto", "none"];
604
+ return ["auto", ...cap.levels];
605
+ }
606
+ routerValue() {
607
+ return this.policyMode === "auto" ? `auto:${this.optimizeFor}` : `model:${this.manualModel}`;
608
+ }
609
+ routerLabel() {
610
+ if (this.policyMode === "auto") return `Auto · ${capitalize(this.optimizeFor)}`;
611
+ if (!this.modelIds.length) return "No models";
612
+ return shortModelLabel(this.manualModel || this.modelIds[0]);
613
+ }
614
+ activeModelRef() {
615
+ const routed = [...this.focusedSession()?.history ?? []].reverse().find((m) => m.role === "assistant" && m.routedModel)?.routedModel;
616
+ if (routed) return routed;
617
+ if (this.policyMode === "manual") return this.manualModel || this.modelIds[0] || cognitiveRuntime.defaultModelId();
618
+ return this.modelIds[0] || cognitiveRuntime.defaultModelId();
619
+ }
620
+ contextUsage() {
621
+ return estimateSessionContextUsage(this.focusedSession(), this.activeModelRef());
622
+ }
623
+ async compactFocused() {
624
+ if (!this.focusedId) return;
625
+ await cognitiveRuntime.compactSession(this.focusedId);
626
+ this.syncSessions();
627
+ }
628
+ onRouterSelect(value) {
629
+ if (value.startsWith("auto:")) {
630
+ const optimize = value.slice(5);
631
+ if (optimize === "cost" || optimize === "balance" || optimize === "intelligence") {
632
+ this.policyMode = "auto";
633
+ this.optimizeFor = optimize;
634
+ }
635
+ return;
636
+ }
637
+ if (value.startsWith("model:")) {
638
+ const model = value.slice(6);
639
+ if (!model) return;
640
+ this.policyMode = "manual";
641
+ this.manualModel = model;
642
+ }
643
+ }
644
+ applyPolicy() {
645
+ const policy = this.policyMode === "manual" ? {
646
+ mode: "manual",
647
+ model: this.manualModel || cognitiveRuntime.defaultModelId(),
648
+ effort: this.manualEffort
649
+ } : {
650
+ mode: "auto",
651
+ optimizeFor: this.optimizeFor
652
+ };
653
+ cognitiveRuntime.setModelPolicy(policy);
654
+ }
655
+ async clearComposer() {
656
+ this.draft = "";
657
+ this.pendingAttachmentIds = [];
658
+ await this.updateComplete;
659
+ if (this.textarea) this.textarea.value = "";
660
+ if (this.fileInput) this.fileInput.value = "";
661
+ }
662
+ sessionAttachments() {
663
+ return this.focusedSession()?.attachments ?? [];
664
+ }
665
+ previewAttachment() {
666
+ if (!this.previewAttachmentId) return void 0;
667
+ return this.sessionAttachments().find((a) => a.id === this.previewAttachmentId);
668
+ }
669
+ openAttachmentPreview(attachmentId) {
670
+ this.previewAttachmentId = attachmentId;
671
+ }
672
+ closeAttachmentPreview() {
673
+ this.previewAttachmentId = null;
674
+ }
675
+ removeSessionAttachment(attachmentId) {
676
+ const sessionId = this.focusedId ?? cognitiveRuntime.ensureSessionId();
677
+ cognitiveRuntime.removeAttachment(sessionId, attachmentId);
678
+ this.pendingAttachmentIds = this.pendingAttachmentIds.filter((id) => id !== attachmentId);
679
+ if (this.previewAttachmentId === attachmentId) this.previewAttachmentId = null;
680
+ this.syncSessions();
681
+ }
682
+ async addFiles(fileList) {
683
+ const files = [...fileList];
684
+ if (!files.length) return;
685
+ const sessionId = cognitiveRuntime.ensureSessionId(this.focusedId ?? void 0);
686
+ this.focusedId = sessionId;
687
+ const existing = this.sessionAttachments();
688
+ const accepted = [];
689
+ for (const file of files) {
690
+ if (existing.length + accepted.length >= 8) {
691
+ toastError(`At most 8 files per session`);
692
+ break;
693
+ }
694
+ if (existing.some((a) => a.name === file.name && a.byteLength === file.size) || accepted.some((a) => a.name === file.name && a.byteLength === file.size)) continue;
695
+ const result = await readPromptAttachmentFromFile(file);
696
+ if (!result.ok) {
697
+ toastError(result.error);
698
+ continue;
699
+ }
700
+ accepted.push(result.attachment);
701
+ }
702
+ const stored = cognitiveRuntime.addAttachments(sessionId, accepted);
703
+ if (stored.length) this.pendingAttachmentIds = [...this.pendingAttachmentIds, ...stored.map((a) => a.id).filter((id) => !this.pendingAttachmentIds.includes(id))];
704
+ this.syncSessions();
705
+ }
706
+ openFilePicker() {
707
+ this.fileInput?.click();
708
+ }
709
+ async onFileInputChange(e) {
710
+ const input = e.target;
711
+ if (input.files?.length) await this.addFiles(input.files);
712
+ input.value = "";
713
+ }
714
+ onComposerDragOver(e) {
715
+ if (!e.dataTransfer?.types.includes("Files")) return;
716
+ e.preventDefault();
717
+ this.dragOver = true;
718
+ }
719
+ onComposerDragLeave(e) {
720
+ if (e.currentTarget === e.target) this.dragOver = false;
721
+ }
722
+ async onComposerDrop(e) {
723
+ e.preventDefault();
724
+ this.dragOver = false;
725
+ if (e.dataTransfer?.files?.length) await this.addFiles(e.dataTransfer.files);
726
+ }
727
+ async send() {
728
+ const text = this.draft.trim();
729
+ const attachmentIds = [...this.pendingAttachmentIds];
730
+ if (!text && !attachmentIds.length || this.busy) return;
731
+ this.applyPolicy();
732
+ await this.clearComposer();
733
+ this.busy = true;
734
+ this.streamingParts = [];
735
+ try {
736
+ await cognitiveRuntime.prompt({
737
+ sessionId: this.focusedId ?? void 0,
738
+ text,
739
+ attachmentIds
740
+ });
741
+ } finally {
742
+ this.busy = false;
743
+ this.syncSessions();
744
+ this.scheduleScrollToBottom();
745
+ this.textarea?.focus();
746
+ }
747
+ }
748
+ renderAttachmentDropdown() {
749
+ const attachments = this.sessionAttachments();
750
+ const count = attachments.length;
751
+ return html`
752
+ <wa-dropdown class="attach-menu" size="s" placement="top-end" distance="4">
753
+ <wa-button
754
+ slot="trigger"
755
+ size="s"
756
+ appearance="plain"
757
+ title=${count ? `${count} attachment${count === 1 ? "" : "s"}` : "Attachments"}
758
+ ?disabled=${this.busy}
759
+ >
760
+ <wa-icon name="paperclip" label="Attachments"></wa-icon>
761
+ ${count > 0 ? html`<wa-badge pill>${count}</wa-badge>` : nothing}
762
+ </wa-button>
763
+ <div
764
+ class="attach-panel"
765
+ @click=${(e) => e.stopPropagation()}
766
+ @keydown=${(e) => e.stopPropagation()}
767
+ >
768
+ ${count ? html`
769
+ <div class="attach-list">
770
+ ${attachments.map((file) => html`
771
+ <div class="attach-row">
772
+ <button
773
+ type="button"
774
+ class="attach-preview-btn"
775
+ title="Open ${file.name}"
776
+ @click=${() => this.openAttachmentPreview(file.id)}
777
+ >
778
+ ${file.kind === "image" && file.dataUrl ? html`<img
779
+ class="attach-thumb"
780
+ src=${file.dataUrl}
781
+ alt=${file.name}
782
+ />` : html`
783
+ <span class="attach-file-icon">
784
+ <wa-icon name="file" label=""></wa-icon>
785
+ </span>
786
+ `}
787
+ <span class="attach-name">${file.name}</span>
788
+ </button>
789
+ <wa-button
790
+ size="s"
791
+ appearance="plain"
792
+ title="Remove from session"
793
+ ?disabled=${this.busy}
794
+ @click=${() => this.removeSessionAttachment(file.id)}
795
+ >
796
+ <wa-icon name="xmark" label="Remove"></wa-icon>
797
+ </wa-button>
798
+ </div>
799
+ `)}
800
+ </div>
801
+ ` : html`<div class="attach-empty">No attachments in this session</div>`}
802
+ <wa-button
803
+ size="s"
804
+ appearance="outlined"
805
+ ?disabled=${this.busy || count >= 8}
806
+ @click=${() => this.openFilePicker()}
807
+ >
808
+ <wa-icon slot="start" name="plus"></wa-icon>
809
+ Add files…
810
+ </wa-button>
811
+ </div>
812
+ </wa-dropdown>
813
+ `;
814
+ }
815
+ renderAttachmentDialog() {
816
+ const preview = this.previewAttachment();
817
+ if (!preview) return nothing;
818
+ return html`
819
+ <wa-dialog
820
+ label=${preview.name}
821
+ open
822
+ light-dismiss
823
+ with-footer
824
+ style="--width: min(40rem, 94vw)"
825
+ @wa-after-hide=${() => this.closeAttachmentPreview()}
826
+ >
827
+ <div class="attach-dialog-body">
828
+ ${preview.kind === "image" && preview.dataUrl ? html`<img class="attach-dialog-image" src=${preview.dataUrl} alt=${preview.name} />` : nothing}
829
+ ${preview.text ? html`<pre class="attach-dialog-text">${preview.text}</pre>` : preview.kind === "image" && preview.dataUrl ? nothing : html`<pre class="attach-dialog-text"></pre>`}
830
+ </div>
831
+ <div slot="footer">
832
+ <wa-button
833
+ variant="danger"
834
+ appearance="outlined"
835
+ @click=${() => this.removeSessionAttachment(preview.id)}
836
+ >
837
+ Remove
838
+ </wa-button>
839
+ <wa-button variant="brand" @click=${() => this.closeAttachmentPreview()}>Close</wa-button>
840
+ </div>
841
+ </wa-dialog>
842
+ `;
843
+ }
844
+ resolvePermission(action) {
845
+ if (!this.pendingPermission) return;
846
+ cognitiveRuntime.resolvePermission(this.pendingPermission.requestId, action);
847
+ this.pendingPermission = null;
848
+ }
849
+ renderToolbar() {
850
+ const title = this.focusedSession()?.title || "Chat";
851
+ return html`
852
+ <span class="toolbar-title" title=${title}>${title}</span>
853
+ <wa-button size="s" appearance="plain" title="New chat" @click=${() => this.newSession()}>
854
+ <wa-icon name="plus" label="New chat"></wa-icon>
855
+ </wa-button>
856
+ ${this.renderHistoryDropdown()}
857
+ <wa-button
858
+ size="s"
859
+ appearance="plain"
860
+ title="Checkpoint"
861
+ @click=${() => {
862
+ if (!this.focusedId) return;
863
+ cognitiveRuntime.sessions.checkpoint(this.focusedId);
864
+ this.syncSessions();
865
+ cognitiveRuntime.commitSessions();
866
+ }}
867
+ >
868
+ <wa-icon name="floppy-disk" label="Checkpoint"></wa-icon>
869
+ </wa-button>
870
+ ${this.renderSettingsDropdown()}
871
+ `;
872
+ }
873
+ contextRingStyle(level) {
874
+ const tone = level === "critical" ? "danger" : level === "warn" ? "warning" : "success";
875
+ return [`--track-color: var(--wa-color-${tone}-fill-quiet)`, `--indicator-color: var(--wa-color-${tone}-fill-loud)`].join(";");
876
+ }
877
+ renderContextDropdown() {
878
+ const usage = this.contextUsage();
879
+ const canCompact = (this.focusedSession()?.history.length ?? 0) > 12;
880
+ const percentLabel = formatContextPercent(usage.percent);
881
+ const title = `Context ${percentLabel} · ${formatTokenCount(usage.used)} / ${formatTokenCount(usage.limit)} tokens`;
882
+ const ringValue = Math.min(100, Math.max(usage.percent, usage.used > 0 ? 1 : 0));
883
+ return html`
884
+ <wa-dropdown
885
+ class="context-menu"
886
+ size="s"
887
+ placement="top-end"
888
+ distance="4"
889
+ >
890
+ <wa-button
891
+ slot="trigger"
892
+ appearance="plain"
893
+ size="s"
894
+ title=${title}
895
+ >
896
+ <wa-progress-ring
897
+ class="context-ring"
898
+ style=${this.contextRingStyle(usage.level)}
899
+ value=${ringValue}
900
+ label=${title}
901
+ ></wa-progress-ring>
902
+ </wa-button>
903
+
904
+ <div
905
+ class="context-panel"
906
+ @click=${(e) => e.stopPropagation()}
907
+ @keydown=${(e) => e.stopPropagation()}
908
+ >
909
+ <div class="context-header">
910
+ <strong>Context tokens</strong>
911
+ <span>${percentLabel}</span>
912
+ </div>
913
+ <div class="context-bars">
914
+ ${usage.buckets.map((bucket) => {
915
+ const share = usage.limit > 0 ? bucket.tokens / usage.limit * 100 : 0;
916
+ return html`
917
+ <div class="context-row">
918
+ <span class="context-row-label">${bucket.label}</span>
919
+ <span>${formatBucketDetail(bucket)}</span>
920
+ <div class="context-meter">
921
+ <span
922
+ style="width:${Math.max(share, bucket.tokens > 0 ? .5 : 0)}%; --bucket-color: var(--wa-color-brand-fill-normal)"
923
+ ></span>
924
+ </div>
925
+ </div>
926
+ `;
927
+ })}
928
+ </div>
929
+ <div class="context-footer">
930
+ <span class="context-remaining">
931
+ ~${formatTokenCount(usage.used)} used · ${formatTokenCount(usage.remaining)} free of
932
+ ${formatTokenCount(usage.limit)}
933
+ · ${shortModelLabel(usage.modelRef)}
934
+ </span>
935
+ <wa-button
936
+ size="s"
937
+ variant=${usage.level === "critical" ? "danger" : "neutral"}
938
+ appearance=${canCompact ? "filled" : "outlined"}
939
+ ?disabled=${!canCompact || this.busy}
940
+ @click=${() => this.compactFocused()}
941
+ >
942
+ <wa-icon slot="start" name="arrows-down-to-line"></wa-icon>
943
+ Compact conversation
944
+ </wa-button>
945
+ </div>
946
+ </div>
947
+ </wa-dropdown>
948
+ `;
949
+ }
950
+ renderContent() {
951
+ const session = this.focusedSession();
952
+ return html`
953
+ <div class="cognitive-shell">
954
+ <wa-scroller class="transcript" orientation="vertical">
955
+ <div class="transcript-inner">
956
+ <div class="transcript-spacer" aria-hidden="true"></div>
957
+ ${session ? this.renderTranscript(session) : nothing}
958
+ </div>
959
+ </wa-scroller>
960
+
961
+ <div
962
+ class="composer-shell ${this.dragOver ? "drag-over" : ""}"
963
+ @dragover=${(e) => this.onComposerDragOver(e)}
964
+ @dragleave=${(e) => this.onComposerDragLeave(e)}
965
+ @drop=${(e) => void this.onComposerDrop(e)}
966
+ >
967
+ <input
968
+ id="attach-file-input"
969
+ type="file"
970
+ multiple
971
+ accept=${ATTACHMENT_FILE_ACCEPT}
972
+ hidden
973
+ @change=${(e) => void this.onFileInputChange(e)}
974
+ />
975
+ <div class="composer">
976
+ <div class="composer-row">
977
+ <wa-textarea
978
+ placeholder="Enter your message here..."
979
+ size="s"
980
+ resize="auto"
981
+ rows="1"
982
+ .value=${this.draft}
983
+ ?disabled=${this.busy}
984
+ @input=${(e) => {
985
+ this.draft = e.target.value;
986
+ }}
987
+ @keydown=${(e) => {
988
+ if (e.key === "Enter" && !e.shiftKey) {
989
+ e.preventDefault();
990
+ this.send();
991
+ }
992
+ }}
993
+ ></wa-textarea>
994
+ <div class="composer-actions">
995
+ ${this.renderAttachmentDropdown()}
996
+ ${this.renderContextDropdown()}
997
+ </div>
998
+ </div>
999
+ </div>
1000
+ </div>
1001
+ ${this.renderAttachmentDialog()}
1002
+ </div>
1003
+ `;
1004
+ }
1005
+ renderTranscript(session) {
1006
+ const showStreaming = this.streamingParts.length > 0;
1007
+ const showPermission = this.pendingPermission && this.pendingPermission.sessionId === session.id;
1008
+ return html`
1009
+ ${session.history.map((m) => this.renderMessage(m))}
1010
+ ${showStreaming ? this.renderAssistantMessage({
1011
+ id: "streaming",
1012
+ role: "assistant",
1013
+ parts: this.streamingParts,
1014
+ createdAt: Date.now()
1015
+ }, { streaming: true }) : nothing}
1016
+ ${showPermission ? html`
1017
+ <wa-callout variant="warning" appearance="outlined">
1018
+ <wa-icon slot="icon" name="shield-halved"></wa-icon>
1019
+ Allow tool <code>${this.pendingPermission.toolName}</code>?
1020
+ <pre class="args">${JSON.stringify(this.pendingPermission.args, null, 2)}</pre>
1021
+ <div class="approval-actions">
1022
+ <wa-button size="s" variant="success" @click=${() => this.resolvePermission("allow")}>
1023
+ <wa-icon slot="start" name="check"></wa-icon>
1024
+ Allow
1025
+ </wa-button>
1026
+ <wa-button
1027
+ size="s"
1028
+ variant="neutral"
1029
+ appearance="outlined"
1030
+ @click=${() => this.resolvePermission("deny")}
1031
+ >
1032
+ <wa-icon slot="start" name="xmark"></wa-icon>
1033
+ Deny
1034
+ </wa-button>
1035
+ </div>
1036
+ </wa-callout>
1037
+ ` : nothing}
1038
+ `;
1039
+ }
1040
+ renderHistoryDropdown() {
1041
+ const groups = groupSessionsByRecency(this.historySessions());
1042
+ return html`
1043
+ <wa-dropdown
1044
+ class="history-menu"
1045
+ size="s"
1046
+ placement="bottom-end"
1047
+ distance="4"
1048
+ ?open=${this.historyOpen}
1049
+ @wa-after-show=${() => {
1050
+ this.historyOpen = true;
1051
+ }}
1052
+ @wa-after-hide=${() => {
1053
+ this.historyOpen = false;
1054
+ this.historyQuery = "";
1055
+ }}
1056
+ >
1057
+ <wa-button
1058
+ slot="trigger"
1059
+ appearance="plain"
1060
+ size="s"
1061
+ with-caret
1062
+ title="Chat history"
1063
+ @click=${(e) => {
1064
+ e.stopPropagation();
1065
+ this.historyOpen = !this.historyOpen;
1066
+ }}
1067
+ >
1068
+ <wa-icon name="clock-rotate-left" label="Chat history"></wa-icon>
1069
+ </wa-button>
1070
+
1071
+ <div
1072
+ class="history-search"
1073
+ @click=${(e) => e.stopPropagation()}
1074
+ @keydown=${(e) => e.stopPropagation()}
1075
+ >
1076
+ <wa-input
1077
+ size="s"
1078
+ clearable
1079
+ placeholder="Search chats…"
1080
+ .value=${this.historyQuery}
1081
+ @input=${(e) => {
1082
+ this.historyQuery = e.target.value;
1083
+ }}
1084
+ >
1085
+ <wa-icon slot="start" name="magnifying-glass"></wa-icon>
1086
+ </wa-input>
1087
+ </div>
1088
+
1089
+ ${groups.length ? groups.map((group) => html`
1090
+ <h3>${group.label}</h3>
1091
+ ${group.sessions.map((s) => html`
1092
+ <wa-dropdown-item @click=${() => this.openSession(s.id)}>
1093
+ <wa-icon
1094
+ slot="icon"
1095
+ name=${s.id === this.focusedId ? "check" : "message"}
1096
+ ></wa-icon>
1097
+ <span class="history-item-title" title=${s.title}>${s.title}</span>
1098
+ <div slot="details" style="display:flex;align-items:center;gap:0.25rem">
1099
+ <span class="history-item-meta">${relativeTime(s.updatedAt)}</span>
1100
+ <wa-button
1101
+ appearance="plain"
1102
+ size="s"
1103
+ title="Delete chat"
1104
+ @click=${(e) => {
1105
+ e.stopPropagation();
1106
+ this.deleteSession(s.id);
1107
+ }}
1108
+ >
1109
+ <wa-icon name="trash" label="Delete chat"></wa-icon>
1110
+ </wa-button>
1111
+ </div>
1112
+ </wa-dropdown-item>
1113
+ `)}
1114
+ `) : html`<div class="history-empty">${this.historyQuery ? "No matching chats" : "No chats yet"}</div>`}
1115
+ </wa-dropdown>
1116
+ `;
1117
+ }
1118
+ onSettingsSelect(value) {
1119
+ if (value === "settings") {
1120
+ this.executeCommand(OPEN_AI_SETTINGS_COMMAND, {});
1121
+ return;
1122
+ }
1123
+ if (value.startsWith("approval:")) {
1124
+ const mode = value.slice(9);
1125
+ if (mode === "manual" || mode === "auto_safe" || mode === "auto_all") {
1126
+ this.approvalMode = mode;
1127
+ cognitiveRuntime.setApprovalMode(mode);
1128
+ }
1129
+ return;
1130
+ }
1131
+ if (value.startsWith("effort:")) {
1132
+ this.manualEffort = value.slice(7) || "auto";
1133
+ return;
1134
+ }
1135
+ this.onRouterSelect(value);
1136
+ }
1137
+ renderSettingsDropdown() {
1138
+ const selected = this.routerValue();
1139
+ const autoOptions = [
1140
+ {
1141
+ value: "auto:cost",
1142
+ label: "Auto · Cost"
1143
+ },
1144
+ {
1145
+ value: "auto:balance",
1146
+ label: "Auto · Balance"
1147
+ },
1148
+ {
1149
+ value: "auto:intelligence",
1150
+ label: "Auto · Intelligence"
1151
+ }
1152
+ ];
1153
+ const effortOptions = this.effortChoices();
1154
+ return html`
1155
+ <wa-dropdown
1156
+ class="settings-menu"
1157
+ size="s"
1158
+ placement="bottom-end"
1159
+ distance="4"
1160
+ @wa-select=${(e) => {
1161
+ const value = e.detail?.item?.value;
1162
+ if (value != null) this.onSettingsSelect(value);
1163
+ }}
1164
+ >
1165
+ <wa-button
1166
+ slot="trigger"
1167
+ appearance="plain"
1168
+ size="s"
1169
+ with-caret
1170
+ title=${`AI · ${this.routerLabel()}`}
1171
+ >
1172
+ <wa-icon name="gear" label="AI settings"></wa-icon>
1173
+ </wa-button>
1174
+
1175
+ <h3>Auto</h3>
1176
+ ${autoOptions.map((option) => html`
1177
+ <wa-dropdown-item
1178
+ value=${option.value}
1179
+ type="checkbox"
1180
+ ?checked=${option.value === selected}
1181
+ >
1182
+ ${option.label}
1183
+ </wa-dropdown-item>
1184
+ `)}
1185
+
1186
+ <wa-divider></wa-divider>
1187
+ <h3>Models</h3>
1188
+ ${this.modelIds.length ? this.modelIds.map((id) => html`
1189
+ <wa-dropdown-item
1190
+ value=${`model:${id}`}
1191
+ type="checkbox"
1192
+ ?checked=${selected === `model:${id}`}
1193
+ >
1194
+ ${id}
1195
+ </wa-dropdown-item>
1196
+ `) : html`<wa-dropdown-item disabled>No models configured</wa-dropdown-item>`}
1197
+
1198
+ ${this.policyMode === "manual" ? html`
1199
+ <wa-divider></wa-divider>
1200
+ <h3>Effort</h3>
1201
+ ${effortOptions.map((level) => html`
1202
+ <wa-dropdown-item
1203
+ value=${`effort:${level}`}
1204
+ type="checkbox"
1205
+ ?checked=${this.manualEffort === level}
1206
+ >
1207
+ ${level}
1208
+ </wa-dropdown-item>
1209
+ `)}
1210
+ ` : nothing}
1211
+
1212
+ <wa-divider></wa-divider>
1213
+ <h3>Tool approvals</h3>
1214
+ ${APPROVAL_OPTIONS$1.map((option) => html`
1215
+ <wa-dropdown-item
1216
+ value=${`approval:${option.value}`}
1217
+ type="checkbox"
1218
+ ?checked=${this.approvalMode === option.value}
1219
+ >
1220
+ ${option.label}
1221
+ </wa-dropdown-item>
1222
+ `)}
1223
+
1224
+ <wa-divider></wa-divider>
1225
+ <wa-dropdown-item value="settings">
1226
+ <wa-icon slot="icon" name="sliders"></wa-icon>
1227
+ Settings…
1228
+ </wa-dropdown-item>
1229
+ </wa-dropdown>
1230
+ `;
1231
+ }
1232
+ resolveAttachmentDataUrl(fileId) {
1233
+ return this.focusedSession()?.attachments?.find((a) => a.id === fileId)?.dataUrl;
1234
+ }
1235
+ renderMessage(message) {
1236
+ if (message.role === "user") {
1237
+ const files = message.parts.filter((p) => p.type === "file");
1238
+ const texts = message.parts.filter((p) => p.type === "text");
1239
+ return html`
1240
+ <div class="msg user">
1241
+ ${files.length ? html`
1242
+ <div class="msg-files">
1243
+ ${files.map((file) => {
1244
+ const dataUrl = file.kind === "image" ? this.resolveAttachmentDataUrl(file.id) : void 0;
1245
+ return dataUrl ? html`<img
1246
+ class="msg-file-thumb"
1247
+ src=${dataUrl}
1248
+ alt=${file.name}
1249
+ title=${`${file.name} (${file.id})`}
1250
+ />` : html`
1251
+ <wa-tag
1252
+ size="s"
1253
+ appearance="outlined"
1254
+ title=${`${file.path || file.name} (${file.id})`}
1255
+ >
1256
+ <wa-icon slot="start" name=${file.kind === "image" ? "image" : "file"}></wa-icon>
1257
+ ${file.name}
1258
+ </wa-tag>
1259
+ `;
1260
+ })}
1261
+ </div>
1262
+ ` : nothing}
1263
+ ${texts.map((p) => this.renderTextPart(p))}
1264
+ ${this.renderMessageMeta(message)}
1265
+ </div>
1266
+ `;
1267
+ }
1268
+ return this.renderAssistantMessage(message);
1269
+ }
1270
+ renderAssistantMessage(message, opts = {}) {
1271
+ const textParts = message.parts.filter((p) => p.type === "text");
1272
+ const techParts = message.parts.filter((p) => p.type !== "text");
1273
+ const open = opts.streaming === true || techParts.some((p) => (p.type === "reasoning" || p.type === "tool") && p.status === "streaming");
1274
+ const summary = html`
1275
+ <div slot="summary" class="msg-summary">
1276
+ ${opts.streaming ? html`
1277
+ <div class="streaming-meta">
1278
+ <wa-spinner style="font-size: 0.85rem"></wa-spinner>
1279
+ <span>streaming…</span>
1280
+ </div>
1281
+ ` : nothing}
1282
+ ${textParts.length ? textParts.map((p) => this.renderTextPart(p)) : html`<div class="bubble">${technicalSummary(techParts)}</div>`}
1283
+ </div>
1284
+ `;
1285
+ if (!techParts.length) return html`
1286
+ <div class="msg assistant">
1287
+ ${opts.streaming ? html`
1288
+ <div class="streaming-meta">
1289
+ <wa-spinner style="font-size: 0.85rem"></wa-spinner>
1290
+ <span>streaming…</span>
1291
+ </div>
1292
+ ` : nothing}
1293
+ ${textParts.map((p) => this.renderTextPart(p))}
1294
+ ${message.id !== "streaming" ? this.renderMessageMeta(message) : nothing}
1295
+ </div>
1296
+ `;
1297
+ return html`
1298
+ <div class="msg assistant">
1299
+ <wa-details class="msg-details" appearance="plain" ?open=${open}>
1300
+ ${summary}
1301
+ <div class="msg-tech">${techParts.map((p) => this.renderTechnicalPart(p))}</div>
1302
+ </wa-details>
1303
+ ${message.id !== "streaming" ? this.renderMessageMeta(message) : nothing}
1304
+ </div>
1305
+ `;
1306
+ }
1307
+ renderMessageMeta(message) {
1308
+ const timeLabel = formatMessageTime(message.createdAt);
1309
+ return html`
1310
+ <div class="msg-meta">
1311
+ <wa-tag size="s" appearance="outlined" title=${new Date(message.createdAt).toLocaleString()}>
1312
+ ${timeLabel}
1313
+ </wa-tag>
1314
+ ${message.routedModel ? html`<wa-tag size="s" appearance="outlined">${message.routedModel}</wa-tag>` : nothing}
1315
+ ${message.effort ? html`<wa-tag size="s" appearance="outlined">${message.effort}</wa-tag>` : nothing}
1316
+ </div>
1317
+ `;
1318
+ }
1319
+ renderTextPart(part) {
1320
+ if (part.type === "text") return html`<div class="bubble">${part.text}</div>`;
1321
+ return nothing;
1322
+ }
1323
+ renderTechnicalPart(part) {
1324
+ if (part.type === "reasoning") return html`
1325
+ <div>
1326
+ <div class="tool-heading">Thinking</div>
1327
+ <div class="bubble">${part.text}</div>
1328
+ </div>
1329
+ `;
1330
+ if (part.type === "tool") {
1331
+ const heading = `${part.name} · ${part.status}${part.durationMs != null ? ` · ${part.durationMs}ms` : ""}`;
1332
+ return html`
1333
+ <div>
1334
+ <div class="tool-heading">${heading}</div>
1335
+ <pre class="args">${JSON.stringify({
1336
+ args: part.args,
1337
+ result: part.result
1338
+ }, null, 2)}</pre>
1339
+ </div>
1340
+ `;
1341
+ }
1342
+ if (part.type === "step") return html`<div class="tool-heading">Step: ${part.title} · ${part.status}</div>`;
1343
+ return nothing;
1344
+ }
1345
+ };
1346
+ _decorate([query("wa-textarea")], DocksCognitiveView.prototype, "textarea", void 0);
1347
+ _decorate([query("#attach-file-input")], DocksCognitiveView.prototype, "fileInput", void 0);
1348
+ _decorate([state()], DocksCognitiveView.prototype, "sessions", void 0);
1349
+ _decorate([state()], DocksCognitiveView.prototype, "focusedId", void 0);
1350
+ _decorate([state()], DocksCognitiveView.prototype, "draft", void 0);
1351
+ _decorate([state()], DocksCognitiveView.prototype, "pendingAttachmentIds", void 0);
1352
+ _decorate([state()], DocksCognitiveView.prototype, "dragOver", void 0);
1353
+ _decorate([state()], DocksCognitiveView.prototype, "previewAttachmentId", void 0);
1354
+ _decorate([state()], DocksCognitiveView.prototype, "busy", void 0);
1355
+ _decorate([state()], DocksCognitiveView.prototype, "pendingPermission", void 0);
1356
+ _decorate([state()], DocksCognitiveView.prototype, "policyMode", void 0);
1357
+ _decorate([state()], DocksCognitiveView.prototype, "optimizeFor", void 0);
1358
+ _decorate([state()], DocksCognitiveView.prototype, "manualModel", void 0);
1359
+ _decorate([state()], DocksCognitiveView.prototype, "manualEffort", void 0);
1360
+ _decorate([state()], DocksCognitiveView.prototype, "approvalMode", void 0);
1361
+ _decorate([state()], DocksCognitiveView.prototype, "modelIds", void 0);
1362
+ _decorate([state()], DocksCognitiveView.prototype, "streamingParts", void 0);
1363
+ _decorate([state()], DocksCognitiveView.prototype, "historyOpen", void 0);
1364
+ _decorate([state()], DocksCognitiveView.prototype, "historyQuery", void 0);
1365
+ DocksCognitiveView = _decorate([customElement("docks-cognitive-view")], DocksCognitiveView);
1366
+ var APPROVAL_OPTIONS$1 = [
1367
+ {
1368
+ value: "manual",
1369
+ label: "Manual"
1370
+ },
1371
+ {
1372
+ value: "auto_safe",
1373
+ label: "Auto-approve non-critical"
1374
+ },
1375
+ {
1376
+ value: "auto_all",
1377
+ label: "Approve all"
1378
+ }
1379
+ ];
1380
+ function capitalize(value) {
1381
+ return value ? value[0].toUpperCase() + value.slice(1) : value;
1382
+ }
1383
+ function technicalSummary(parts) {
1384
+ const tools = parts.filter((p) => p.type === "tool");
1385
+ const reasoning = parts.some((p) => p.type === "reasoning");
1386
+ const steps = parts.filter((p) => p.type === "step");
1387
+ const bits = [];
1388
+ if (tools.length) bits.push(tools.map((t) => `${t.name} · ${t.status}${t.durationMs != null ? ` · ${t.durationMs}ms` : ""}`).join(", "));
1389
+ if (reasoning) bits.push("Thinking");
1390
+ if (steps.length) bits.push(`${steps.length} step${steps.length === 1 ? "" : "s"}`);
1391
+ return bits.join(" · ") || "Details";
1392
+ }
1393
+ function startOfDay(ts) {
1394
+ const d = new Date(ts);
1395
+ d.setHours(0, 0, 0, 0);
1396
+ return d.getTime();
1397
+ }
1398
+ function groupSessionsByRecency(sessions) {
1399
+ const today = startOfDay(Date.now());
1400
+ const yesterday = today - 864e5;
1401
+ const week = today - 6048e5;
1402
+ const buckets = {
1403
+ today: [],
1404
+ yesterday: [],
1405
+ week: [],
1406
+ older: []
1407
+ };
1408
+ for (const s of sessions) {
1409
+ const day = startOfDay(s.updatedAt);
1410
+ if (day >= today) buckets.today.push(s);
1411
+ else if (day >= yesterday) buckets.yesterday.push(s);
1412
+ else if (day >= week) buckets.week.push(s);
1413
+ else buckets.older.push(s);
1414
+ }
1415
+ return [
1416
+ {
1417
+ label: "Today",
1418
+ sessions: buckets.today
1419
+ },
1420
+ {
1421
+ label: "Yesterday",
1422
+ sessions: buckets.yesterday
1423
+ },
1424
+ {
1425
+ label: "Previous 7 Days",
1426
+ sessions: buckets.week
1427
+ },
1428
+ {
1429
+ label: "Older",
1430
+ sessions: buckets.older
1431
+ }
1432
+ ].filter((g) => g.sessions.length > 0);
1433
+ }
1434
+ function relativeTime(ts) {
1435
+ const delta = Math.max(0, Date.now() - ts);
1436
+ const mins = Math.floor(delta / 6e4);
1437
+ if (mins < 1) return "now";
1438
+ if (mins < 60) return `${mins}m`;
1439
+ const hours = Math.floor(mins / 60);
1440
+ if (hours < 24) return `${hours}h`;
1441
+ const days = Math.floor(hours / 24);
1442
+ if (days < 7) return `${days}d`;
1443
+ return new Date(ts).toLocaleDateString(void 0, {
1444
+ month: "short",
1445
+ day: "numeric"
1446
+ });
1447
+ }
1448
+ function formatMessageTime(ts) {
1449
+ const date = new Date(ts);
1450
+ const now = /* @__PURE__ */ new Date();
1451
+ const sameDay = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
1452
+ const time = date.toLocaleTimeString(void 0, {
1453
+ hour: "numeric",
1454
+ minute: "2-digit"
1455
+ });
1456
+ if (sameDay) return time;
1457
+ return `${date.toLocaleDateString(void 0, {
1458
+ month: "short",
1459
+ day: "numeric"
1460
+ })} ${time}`;
1461
+ }
1462
+ function shortModelLabel(modelRef) {
1463
+ const slash = modelRef.indexOf("/");
1464
+ return slash === -1 ? modelRef : modelRef.slice(slash + 1);
1465
+ }
1466
+ function mergePart(parts, part) {
1467
+ const next = [...parts];
1468
+ const last = next[next.length - 1];
1469
+ if (part.type === "text" && last?.type === "text") {
1470
+ next[next.length - 1] = {
1471
+ ...last,
1472
+ text: last.text + part.text
1473
+ };
1474
+ return next;
1475
+ }
1476
+ if (part.type === "reasoning" && last?.type === "reasoning") {
1477
+ next[next.length - 1] = {
1478
+ ...last,
1479
+ text: last.text + part.text,
1480
+ status: part.status
1481
+ };
1482
+ return next;
1483
+ }
1484
+ if (part.type === "tool" && last?.type === "tool" && last.id === part.id) {
1485
+ next[next.length - 1] = part;
1486
+ return next;
1487
+ }
1488
+ next.push(part);
1489
+ return next;
1490
+ }
1491
+ //#endregion
1492
+ //#region src/view/cognitive-settings-editor.ts
1493
+ var APPROVAL_OPTIONS = [
1494
+ {
1495
+ value: "manual",
1496
+ label: "Manual",
1497
+ hint: "Confirm every tool before it runs"
1498
+ },
1499
+ {
1500
+ value: "auto_safe",
1501
+ label: "Auto-approve non-critical",
1502
+ hint: "The agent classifies each tool as safe or critical; only critical ones ask for approval"
1503
+ },
1504
+ {
1505
+ value: "auto_all",
1506
+ label: "Approve all",
1507
+ hint: "Run every tool without asking"
1508
+ }
1509
+ ];
1510
+ var DocksCognitiveSettingsEditor = class DocksCognitiveSettingsEditor extends DocksPart {
1511
+ constructor(..._args) {
1512
+ super(..._args);
1513
+ this.settings = defaultProvidersSettings();
1514
+ this.approvalMode = "auto_safe";
1515
+ this.availableModels = {};
1516
+ this.loadingModels = {};
1517
+ this.modelErrors = {};
1518
+ this.unsubs = [];
1519
+ this.fetchControllers = /* @__PURE__ */ new Map();
1520
+ this.fetchTimers = /* @__PURE__ */ new Map();
1521
+ this.autoFetchedKeys = /* @__PURE__ */ new Set();
1522
+ }
1523
+ doInitUI() {
1524
+ cognitiveRuntime.start();
1525
+ this.loadFromRuntime();
1526
+ this.unsubs.push(cognitiveRuntime.events.subscribe(TOPIC.providersChanged, () => {
1527
+ if (!this.isDirty()) this.loadFromRuntime();
1528
+ }), cognitiveRuntime.events.subscribe(TOPIC.approvalModeChanged, (mode) => {
1529
+ this.approvalMode = mode;
1530
+ }));
1531
+ }
1532
+ disconnectedCallback() {
1533
+ for (const unsub of this.unsubs) unsub();
1534
+ this.unsubs = [];
1535
+ for (const timer of this.fetchTimers.values()) clearTimeout(timer);
1536
+ this.fetchTimers.clear();
1537
+ for (const controller of this.fetchControllers.values()) controller.abort();
1538
+ this.fetchControllers.clear();
1539
+ super.disconnectedCallback();
1540
+ }
1541
+ loadFromRuntime() {
1542
+ this.settings = cognitiveRuntime.getProvidersSettings();
1543
+ this.approvalMode = cognitiveRuntime.getApprovalMode();
1544
+ this.availableModels = {};
1545
+ this.loadingModels = {};
1546
+ this.modelErrors = {};
1547
+ this.autoFetchedKeys.clear();
1548
+ this.markDirty(false);
1549
+ this.settings.providers.forEach((provider, index) => {
1550
+ if (provider.apiKey.trim() && provider.baseUrl.trim()) this.fetchModels(index, { auto: true });
1551
+ });
1552
+ }
1553
+ setApprovalMode(mode) {
1554
+ this.approvalMode = mode;
1555
+ cognitiveRuntime.setApprovalMode(mode);
1556
+ }
1557
+ touch(next) {
1558
+ this.settings = next;
1559
+ this.markDirty(true);
1560
+ }
1561
+ updateProvider(index, patch) {
1562
+ const prev = this.settings.providers[index];
1563
+ const providers = this.settings.providers.map((p, i) => i === index ? {
1564
+ ...p,
1565
+ ...patch
1566
+ } : p);
1567
+ let defaultProviderId = this.settings.defaultProviderId;
1568
+ if (patch.id && prev?.id === defaultProviderId) defaultProviderId = patch.id;
1569
+ this.touch({
1570
+ ...this.settings,
1571
+ defaultProviderId,
1572
+ providers
1573
+ });
1574
+ if (patch.apiKey !== void 0 || patch.baseUrl !== void 0) {
1575
+ const next = providers[index];
1576
+ if (next?.apiKey.trim() && next.baseUrl.trim()) this.scheduleFetchModels(index);
1577
+ else if (!next?.apiKey.trim()) this.clearModels(index);
1578
+ }
1579
+ }
1580
+ clearModels(index) {
1581
+ this.fetchTimers.get(index) && clearTimeout(this.fetchTimers.get(index));
1582
+ this.fetchTimers.delete(index);
1583
+ this.fetchControllers.get(index)?.abort();
1584
+ this.fetchControllers.delete(index);
1585
+ const { [index]: _a, ...available } = this.availableModels;
1586
+ const { [index]: _b, ...loading } = this.loadingModels;
1587
+ const { [index]: _c, ...errors } = this.modelErrors;
1588
+ this.availableModels = available;
1589
+ this.loadingModels = loading;
1590
+ this.modelErrors = errors;
1591
+ }
1592
+ scheduleFetchModels(index) {
1593
+ const existing = this.fetchTimers.get(index);
1594
+ if (existing) clearTimeout(existing);
1595
+ this.fetchTimers.set(index, setTimeout(() => {
1596
+ this.fetchTimers.delete(index);
1597
+ this.fetchModels(index, { auto: true });
1598
+ }, 400));
1599
+ }
1600
+ async fetchModels(index, opts = {}) {
1601
+ const provider = this.settings.providers[index];
1602
+ if (!provider?.baseUrl.trim()) {
1603
+ toastError("Set a base URL before fetching models");
1604
+ return;
1605
+ }
1606
+ if (opts.auto && !provider.apiKey.trim()) return;
1607
+ const fingerprint = `${provider.baseUrl}|${provider.apiKey}`;
1608
+ if (opts.auto && this.autoFetchedKeys.has(fingerprint) && this.availableModels[index]?.length) return;
1609
+ this.fetchControllers.get(index)?.abort();
1610
+ const controller = new AbortController();
1611
+ this.fetchControllers.set(index, controller);
1612
+ this.loadingModels = {
1613
+ ...this.loadingModels,
1614
+ [index]: true
1615
+ };
1616
+ this.modelErrors = {
1617
+ ...this.modelErrors,
1618
+ [index]: ""
1619
+ };
1620
+ try {
1621
+ const models = await fetchOpenAICompatibleModels(provider.baseUrl, provider.apiKey || void 0, controller.signal);
1622
+ if (controller.signal.aborted) return;
1623
+ this.availableModels = {
1624
+ ...this.availableModels,
1625
+ [index]: models
1626
+ };
1627
+ this.autoFetchedKeys.add(fingerprint);
1628
+ if (!opts.auto) toastInfo(`Loaded ${models.length} models`);
1629
+ } catch (err) {
1630
+ if (controller.signal.aborted) return;
1631
+ const message = err instanceof Error ? err.message : String(err);
1632
+ this.modelErrors = {
1633
+ ...this.modelErrors,
1634
+ [index]: message
1635
+ };
1636
+ this.availableModels = {
1637
+ ...this.availableModels,
1638
+ [index]: []
1639
+ };
1640
+ if (!opts.auto) toastError(message);
1641
+ } finally {
1642
+ if (this.fetchControllers.get(index) === controller) {
1643
+ this.fetchControllers.delete(index);
1644
+ this.loadingModels = {
1645
+ ...this.loadingModels,
1646
+ [index]: false
1647
+ };
1648
+ }
1649
+ }
1650
+ }
1651
+ onModelsSelect(index, e) {
1652
+ const value = e.target.value;
1653
+ const selected = Array.isArray(value) ? value : value ? [value] : [];
1654
+ this.updateProvider(index, { models: selected.join(",") });
1655
+ }
1656
+ addProvider() {
1657
+ const id = uniqueProviderId(this.settings.providers);
1658
+ this.touch({
1659
+ ...this.settings,
1660
+ providers: [...this.settings.providers, {
1661
+ id,
1662
+ baseUrl: "",
1663
+ apiKey: "",
1664
+ models: "",
1665
+ enabled: true
1666
+ }],
1667
+ defaultProviderId: this.settings.defaultProviderId || id
1668
+ });
1669
+ }
1670
+ async deleteProvider(index) {
1671
+ const provider = this.settings.providers[index];
1672
+ if (!provider) return;
1673
+ if (!await confirmDialog(`Delete provider "${provider.id || "(unnamed)"}"?`)) return;
1674
+ this.clearModels(index);
1675
+ const providers = this.settings.providers.filter((_, i) => i !== index);
1676
+ const defaultProviderId = this.settings.defaultProviderId === provider.id ? providers.find((p) => p.enabled)?.id ?? "" : this.settings.defaultProviderId;
1677
+ this.touch({
1678
+ providers,
1679
+ defaultProviderId
1680
+ });
1681
+ this.reindexModelState(index);
1682
+ }
1683
+ /** Shift per-row model fetch state after a row is removed. */
1684
+ reindexModelState(removed) {
1685
+ const remap = (src) => {
1686
+ const next = {};
1687
+ for (const [key, value] of Object.entries(src)) {
1688
+ const i = Number(key);
1689
+ if (Number.isNaN(i) || i === removed) continue;
1690
+ next[i > removed ? i - 1 : i] = value;
1691
+ }
1692
+ return next;
1693
+ };
1694
+ this.availableModels = remap(this.availableModels);
1695
+ this.loadingModels = remap(this.loadingModels);
1696
+ this.modelErrors = remap(this.modelErrors);
1697
+ }
1698
+ setDefault(id) {
1699
+ this.touch({
1700
+ ...this.settings,
1701
+ defaultProviderId: id
1702
+ });
1703
+ }
1704
+ async save() {
1705
+ if (!this.isDirty()) return;
1706
+ try {
1707
+ await cognitiveRuntime.setProvidersSettings(this.settings);
1708
+ this.loadFromRuntime();
1709
+ toastInfo("AI providers saved");
1710
+ } catch (err) {
1711
+ toastError(err instanceof Error ? err.message : String(err));
1712
+ }
1713
+ }
1714
+ renderToolbar() {
1715
+ return html`
1716
+ <wa-button size="s" appearance="plain" title="Add provider" @click=${() => this.addProvider()}>
1717
+ <wa-icon name="plus" label="Add provider"></wa-icon>
1718
+ </wa-button>
1719
+ <wa-button
1720
+ size="s"
1721
+ variant="brand"
1722
+ ?disabled=${!this.isDirty()}
1723
+ @click=${() => void this.save()}
1724
+ >
1725
+ <wa-icon slot="start" name="floppy-disk"></wa-icon>
1726
+ Save
1727
+ </wa-button>
1728
+ `;
1729
+ }
1730
+ renderContent() {
1731
+ const rows = this.settings.providers;
1732
+ return html`
1733
+ <div class="editor">
1734
+ <section class="section">
1735
+ <header class="header">
1736
+ <div>
1737
+ <h2>Tool approvals</h2>
1738
+ <p class="hint">How the agent handles tool confirmation — same options as GitHub Copilot.</p>
1739
+ </div>
1740
+ </header>
1741
+ <wa-radio-group
1742
+ name="approval-mode"
1743
+ label="Approval mode"
1744
+ orientation="vertical"
1745
+ .value=${this.approvalMode}
1746
+ @change=${(e) => {
1747
+ const value = e.target.value;
1748
+ if (value === "manual" || value === "auto_safe" || value === "auto_all") this.setApprovalMode(value);
1749
+ }}
1750
+ >
1751
+ ${APPROVAL_OPTIONS.map((option) => html`
1752
+ <wa-radio value=${option.value}>
1753
+ <span class="approval-option">
1754
+ <span class="approval-label">${option.label}</span>
1755
+ <span class="approval-hint">${option.hint}</span>
1756
+ </span>
1757
+ </wa-radio>
1758
+ `)}
1759
+ </wa-radio-group>
1760
+ </section>
1761
+
1762
+ <section class="section">
1763
+ <header class="header">
1764
+ <div>
1765
+ <h2>AI Providers</h2>
1766
+ <p class="hint">OpenAI-compatible endpoints. Edit cells in place, then Save.</p>
1767
+ </div>
1768
+ <wa-button size="s" variant="brand" appearance="filled" @click=${() => this.addProvider()}>
1769
+ <wa-icon slot="start" name="plus"></wa-icon>
1770
+ Add provider
1771
+ </wa-button>
1772
+ </header>
1773
+
1774
+ ${rows.length === 0 ? html`<wa-callout variant="neutral" appearance="outlined">No providers configured.</wa-callout>` : html`
1775
+ <div class="table-wrap">
1776
+ <table class="providers">
1777
+ <thead>
1778
+ <tr>
1779
+ <th class="col-on">On</th>
1780
+ <th class="col-id">ID</th>
1781
+ <th class="col-url">Base URL</th>
1782
+ <th class="col-key">API Key</th>
1783
+ <th class="col-models">Models</th>
1784
+ <th class="col-default">Default</th>
1785
+ <th class="col-actions"></th>
1786
+ </tr>
1787
+ </thead>
1788
+ <tbody>
1789
+ ${rows.map((provider, index) => this.renderRow(provider, index))}
1790
+ </tbody>
1791
+ </table>
1792
+ </div>
1793
+ `}
1794
+
1795
+ <wa-callout variant="neutral" appearance="plain" size="s">
1796
+ <wa-icon slot="icon" name="circle-info"></wa-icon>
1797
+ Enter an API key to load available models. Catalog ids become
1798
+ <code>providerId/modelId</code> for Auto routing.
1799
+ </wa-callout>
1800
+ </section>
1801
+ </div>
1802
+ `;
1803
+ }
1804
+ renderRow(provider, index) {
1805
+ const isDefault = this.settings.defaultProviderId === provider.id;
1806
+ return html`
1807
+ <tr class=${isDefault ? "is-default" : ""}>
1808
+ <td>
1809
+ <wa-checkbox
1810
+ ?checked=${provider.enabled}
1811
+ @change=${(e) => this.updateProvider(index, { enabled: e.target.checked })}
1812
+ ></wa-checkbox>
1813
+ </td>
1814
+ <td>
1815
+ <wa-input
1816
+ size="s"
1817
+ .value=${provider.id}
1818
+ placeholder="openai"
1819
+ @input=${(e) => this.updateProvider(index, { id: e.target.value })}
1820
+ ></wa-input>
1821
+ </td>
1822
+ <td>
1823
+ <wa-input
1824
+ size="s"
1825
+ .value=${provider.baseUrl}
1826
+ placeholder="https://api.openai.com/v1"
1827
+ @input=${(e) => this.updateProvider(index, { baseUrl: e.target.value })}
1828
+ ></wa-input>
1829
+ </td>
1830
+ <td>
1831
+ <wa-input
1832
+ size="s"
1833
+ type="password"
1834
+ password-toggle
1835
+ .value=${provider.apiKey}
1836
+ placeholder="optional"
1837
+ @input=${(e) => this.updateProvider(index, { apiKey: e.target.value })}
1838
+ ></wa-input>
1839
+ </td>
1840
+ <td>${this.renderModelsCell(provider, index)}</td>
1841
+ <td class="col-default">
1842
+ ${isDefault ? html`<wa-tag size="s" variant="brand" pill>Default</wa-tag>` : html`
1843
+ <wa-button
1844
+ size="s"
1845
+ appearance="plain"
1846
+ ?disabled=${!provider.id}
1847
+ @click=${() => this.setDefault(provider.id)}
1848
+ >
1849
+ Set
1850
+ </wa-button>
1851
+ `}
1852
+ </td>
1853
+ <td class="col-actions">
1854
+ <wa-button
1855
+ size="s"
1856
+ appearance="plain"
1857
+ variant="danger"
1858
+ title="Delete"
1859
+ @click=${() => void this.deleteProvider(index)}
1860
+ >
1861
+ <wa-icon name="trash" label="Delete"></wa-icon>
1862
+ </wa-button>
1863
+ </td>
1864
+ </tr>
1865
+ `;
1866
+ }
1867
+ renderModelsCell(provider, index) {
1868
+ const hasKey = Boolean(provider.apiKey.trim());
1869
+ const remote = this.availableModels[index] ?? [];
1870
+ const loading = Boolean(this.loadingModels[index]);
1871
+ const error = this.modelErrors[index];
1872
+ const selected = parseModelIds(provider.models);
1873
+ if (!(hasKey || remote.length > 0)) return html`
1874
+ <div class="models-cell">
1875
+ <wa-input
1876
+ size="s"
1877
+ .value=${provider.models}
1878
+ placeholder="gpt-4o-mini,gpt-4o"
1879
+ @input=${(e) => this.updateProvider(index, { models: e.target.value })}
1880
+ ></wa-input>
1881
+ <wa-button
1882
+ size="s"
1883
+ appearance="plain"
1884
+ title="Fetch models"
1885
+ ?disabled=${!provider.baseUrl.trim() || loading}
1886
+ ?loading=${loading}
1887
+ @click=${() => void this.fetchModels(index)}
1888
+ >
1889
+ <wa-icon name="arrows-rotate" label="Reload models"></wa-icon>
1890
+ </wa-button>
1891
+ </div>
1892
+ `;
1893
+ return html`
1894
+ <div class="models-cell">
1895
+ <wa-select
1896
+ size="s"
1897
+ multiple
1898
+ with-clear
1899
+ placeholder=${loading ? "Loading models…" : "Select models"}
1900
+ .value=${selected}
1901
+ ?disabled=${loading && remote.length === 0}
1902
+ @change=${(e) => this.onModelsSelect(index, e)}
1903
+ >
1904
+ ${remote.map((m) => html`<wa-option value=${m.id}>${m.name === m.id ? m.id : `${m.name} (${m.id})`}</wa-option>`)}
1905
+ ${selected.filter((id) => !remote.some((m) => m.id === id)).map((id) => html`<wa-option value=${id}>${id}</wa-option>`)}
1906
+ </wa-select>
1907
+ <wa-button
1908
+ size="s"
1909
+ appearance="plain"
1910
+ title="Reload models"
1911
+ ?disabled=${!provider.baseUrl.trim() || loading}
1912
+ ?loading=${loading}
1913
+ @click=${() => void this.fetchModels(index)}
1914
+ >
1915
+ <wa-icon name="arrows-rotate" label="Reload models"></wa-icon>
1916
+ </wa-button>
1917
+ </div>
1918
+ ${error ? html`<div class="models-error">${error}</div>` : ""}
1919
+ `;
1920
+ }
1921
+ static {
1922
+ this.styles = css`
1923
+ :host {
1924
+ display: block;
1925
+ height: 100%;
1926
+ }
1927
+
1928
+ .editor {
1929
+ display: flex;
1930
+ flex-direction: column;
1931
+ gap: var(--wa-space-2xl);
1932
+ padding: var(--wa-space-l);
1933
+ min-width: 0;
1934
+ }
1935
+
1936
+ .section {
1937
+ display: flex;
1938
+ flex-direction: column;
1939
+ gap: var(--wa-space-l);
1940
+ min-width: 0;
1941
+ }
1942
+
1943
+ .header {
1944
+ display: flex;
1945
+ justify-content: space-between;
1946
+ align-items: flex-start;
1947
+ gap: var(--wa-space-m);
1948
+ flex-wrap: wrap;
1949
+ }
1950
+
1951
+ .header h2 {
1952
+ margin: 0;
1953
+ font-size: var(--wa-font-size-l);
1954
+ }
1955
+
1956
+ wa-radio-group {
1957
+ max-width: 36rem;
1958
+ }
1959
+
1960
+ .approval-option {
1961
+ display: flex;
1962
+ flex-direction: column;
1963
+ gap: var(--wa-space-3xs);
1964
+ padding-block: var(--wa-space-3xs);
1965
+ }
1966
+
1967
+ .approval-label {
1968
+ font-weight: 500;
1969
+ }
1970
+
1971
+ .approval-hint {
1972
+ color: var(--wa-color-text-quiet);
1973
+ font-size: var(--wa-font-size-s);
1974
+ }
1975
+
1976
+ .hint {
1977
+ margin: var(--wa-space-2xs) 0 0;
1978
+ color: var(--wa-color-text-quiet);
1979
+ font-size: var(--wa-font-size-s);
1980
+ }
1981
+
1982
+ .table-wrap {
1983
+ overflow: auto;
1984
+ border: var(--wa-border-width-s) solid var(--wa-color-border-normal);
1985
+ border-radius: var(--wa-border-radius-m);
1986
+ }
1987
+
1988
+ table.providers {
1989
+ width: 100%;
1990
+ border-collapse: collapse;
1991
+ min-width: 56rem;
1992
+ }
1993
+
1994
+ th,
1995
+ td {
1996
+ padding: var(--wa-space-2xs) var(--wa-space-s);
1997
+ border-bottom: var(--wa-border-width-s) solid var(--wa-color-border-normal);
1998
+ text-align: left;
1999
+ vertical-align: middle;
2000
+ }
2001
+
2002
+ th {
2003
+ font-size: var(--wa-font-size-xs);
2004
+ font-weight: 600;
2005
+ color: var(--wa-color-text-quiet);
2006
+ background: var(--wa-color-surface-lowered);
2007
+ white-space: nowrap;
2008
+ }
2009
+
2010
+ tr.is-default td {
2011
+ background: color-mix(in srgb, var(--wa-color-brand-fill-normal) 8%, transparent);
2012
+ }
2013
+
2014
+ tbody tr:last-child td {
2015
+ border-bottom: none;
2016
+ }
2017
+
2018
+ .col-on {
2019
+ width: 2.5rem;
2020
+ }
2021
+
2022
+ .col-id {
2023
+ width: 8rem;
2024
+ }
2025
+
2026
+ .col-key {
2027
+ width: 11rem;
2028
+ }
2029
+
2030
+ .col-models {
2031
+ min-width: 16rem;
2032
+ }
2033
+
2034
+ .col-default {
2035
+ width: 5.5rem;
2036
+ white-space: nowrap;
2037
+ }
2038
+
2039
+ .col-actions {
2040
+ width: 2.75rem;
2041
+ text-align: end;
2042
+ }
2043
+
2044
+ td wa-input {
2045
+ width: 100%;
2046
+ }
2047
+
2048
+ .models-cell {
2049
+ display: flex;
2050
+ gap: var(--wa-space-2xs);
2051
+ align-items: flex-start;
2052
+ min-width: 14rem;
2053
+ }
2054
+
2055
+ .models-cell wa-input,
2056
+ .models-cell wa-select {
2057
+ flex: 1;
2058
+ min-width: 0;
2059
+ }
2060
+
2061
+ .models-error {
2062
+ margin-top: var(--wa-space-3xs);
2063
+ color: var(--wa-color-danger-on-quiet);
2064
+ font-size: var(--wa-font-size-xs);
2065
+ }
2066
+
2067
+ code {
2068
+ font-family: var(--wa-font-family-code, var(--wa-font-mono));
2069
+ font-size: 0.9em;
2070
+ }
2071
+ `;
2072
+ }
2073
+ };
2074
+ _decorate([property({ attribute: false })], DocksCognitiveSettingsEditor.prototype, "input", void 0);
2075
+ _decorate([state()], DocksCognitiveSettingsEditor.prototype, "settings", void 0);
2076
+ _decorate([state()], DocksCognitiveSettingsEditor.prototype, "approvalMode", void 0);
2077
+ _decorate([state()], DocksCognitiveSettingsEditor.prototype, "availableModels", void 0);
2078
+ _decorate([state()], DocksCognitiveSettingsEditor.prototype, "loadingModels", void 0);
2079
+ _decorate([state()], DocksCognitiveSettingsEditor.prototype, "modelErrors", void 0);
2080
+ DocksCognitiveSettingsEditor = _decorate([customElement("docks-cognitive-settings-editor")], DocksCognitiveSettingsEditor);
2081
+ function uniqueProviderId(existing) {
2082
+ const used = new Set(existing.map((p) => p.id));
2083
+ if (!used.has("provider")) return "provider";
2084
+ let n = 2;
2085
+ while (used.has(`provider-${n}`)) n += 1;
2086
+ return `provider-${n}`;
2087
+ }
2088
+ //#endregion
2089
+ //#region src/cognitive-extension.ts
2090
+ cognitiveRuntime.start();
2091
+ rootContext.put("cognitiveRuntime", cognitiveRuntime);
2092
+ contributionRegistry.registerContribution(SIDEBAR_AUXILIARY, {
2093
+ name: AI_VIEW_NAME,
2094
+ label: "AI",
2095
+ icon: "robot",
2096
+ editorId: AI_VIEW_NAME,
2097
+ component: (id) => html`<docks-cognitive-view id="${id}"></docks-cognitive-view>`
2098
+ });
2099
+ editorRegistry.registerEditorInputHandler({
2100
+ editorId: AI_SETTINGS_EDITOR_ID,
2101
+ label: "AI Settings",
2102
+ ranking: 1e3,
2103
+ canHandle: (input) => input.key === AI_SETTINGS_KEY,
2104
+ handle: async (input) => {
2105
+ input.component = (id) => html`<docks-cognitive-settings-editor id="${id}" .input=${input}></docks-cognitive-settings-editor>`;
2106
+ return input;
2107
+ }
2108
+ });
2109
+ registerAll({
2110
+ command: {
2111
+ id: OPEN_AI_SETTINGS_COMMAND,
2112
+ name: "Open AI Settings",
2113
+ description: "Configure AI model providers",
2114
+ parameters: []
2115
+ },
2116
+ handler: { execute: () => {
2117
+ const editorInput = {
2118
+ title: "AI Settings",
2119
+ data: {},
2120
+ key: AI_SETTINGS_KEY,
2121
+ icon: "gear",
2122
+ state: {}
2123
+ };
2124
+ editorRegistry.loadEditor(editorInput);
2125
+ } }
2126
+ });
2127
+ //#endregion
2128
+
2129
+ //# sourceMappingURL=cognitive-extension-CG_d39K5.js.map