clay-server 5.0.2-beta.1 → 5.1.0-beta.2

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 (162) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/lib/daemon-project-access.js +7 -1
  4. package/lib/daemon.js +2 -0
  5. package/lib/dicebear-avatar.js +62 -0
  6. package/lib/knowledge-db-service.js +175 -0
  7. package/lib/knowledge-db-validation.js +0 -0
  8. package/lib/knowledge-document-index.js +49 -0
  9. package/lib/knowledge-draft-store.js +56 -0
  10. package/lib/knowledge-file-store.js +199 -0
  11. package/lib/knowledge-markdown.js +128 -0
  12. package/lib/mate-knowledge-mcp-server.js +29 -6
  13. package/lib/mate-knowledge-service.js +23 -2
  14. package/lib/mate-knowledge-sync.js +36 -21
  15. package/lib/mate-workspace-color.js +4 -0
  16. package/lib/mates.js +18 -0
  17. package/lib/project-connection.js +36 -2
  18. package/lib/project-debate-proposal.js +6 -0
  19. package/lib/project-debate-workspace.js +43 -0
  20. package/lib/project-debate.js +1 -0
  21. package/lib/project-file-path.js +2 -3
  22. package/lib/project-knowledge.js +315 -136
  23. package/lib/project-mate-creation-proposal.js +1 -0
  24. package/lib/project-request-access.js +13 -1
  25. package/lib/project-sessions.js +39 -2
  26. package/lib/project-user-knowledge.js +60 -0
  27. package/lib/project.js +59 -18
  28. package/lib/public/app.js +73 -34
  29. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  30. package/lib/public/css/clay-workspace.css +105 -0
  31. package/lib/public/css/debates-workbench.css +47 -0
  32. package/lib/public/css/filebrowser.css +10 -0
  33. package/lib/public/css/home-debate-live.css +26 -0
  34. package/lib/public/css/home-hub.css +3 -1
  35. package/lib/public/css/home-mate-model-picker.css +15 -131
  36. package/lib/public/css/home-mate-settings.css +168 -16
  37. package/lib/public/css/icon-strip.css +249 -32
  38. package/lib/public/css/mate-conversation.css +176 -0
  39. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  40. package/lib/public/css/mate-workspace-color.css +16 -0
  41. package/lib/public/css/mates.css +28 -28
  42. package/lib/public/css/messages.css +1 -114
  43. package/lib/public/css/mobile-nav.css +16 -16
  44. package/lib/public/css/overlays.css +8 -0
  45. package/lib/public/css/project-logs.css +7 -0
  46. package/lib/public/css/project-mate-creation.css +105 -0
  47. package/lib/public/css/right-workbench.css +3 -3
  48. package/lib/public/css/session-folders.css +80 -21
  49. package/lib/public/css/sidebar.css +183 -107
  50. package/lib/public/css/title-bar.css +151 -0
  51. package/lib/public/css/worker-delegation.css +47 -0
  52. package/lib/public/css/worker-proposal.css +72 -150
  53. package/lib/public/css/you.css +74 -0
  54. package/lib/public/index.html +33 -101
  55. package/lib/public/modules/account-island.js +230 -0
  56. package/lib/public/modules/app-cursors.js +19 -43
  57. package/lib/public/modules/app-debate-ui.js +4 -3
  58. package/lib/public/modules/app-header.js +106 -0
  59. package/lib/public/modules/app-message-router.js +12 -3
  60. package/lib/public/modules/app-messages.js +34 -9
  61. package/lib/public/modules/app-notifications.js +4 -7
  62. package/lib/public/modules/app-projects.js +5 -38
  63. package/lib/public/modules/app-rendering.js +12 -13
  64. package/lib/public/modules/avatar.js +25 -9
  65. package/lib/public/modules/chat-render-runtime.js +16 -4
  66. package/lib/public/modules/command-palette.js +2 -15
  67. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  68. package/lib/public/modules/debate-controls.js +299 -0
  69. package/lib/public/modules/debate.js +2 -0
  70. package/lib/public/modules/debates-workbench.js +339 -0
  71. package/lib/public/modules/default-ai.js +81 -65
  72. package/lib/public/modules/filebrowser.js +28 -8
  73. package/lib/public/modules/home-debate-controls.js +18 -289
  74. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  75. package/lib/public/modules/home-mate-color-picker.js +66 -0
  76. package/lib/public/modules/home-mate-creation.js +1 -1
  77. package/lib/public/modules/home-mate-instructions.js +90 -0
  78. package/lib/public/modules/home-mate-model-picker.js +80 -271
  79. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  80. package/lib/public/modules/home-mate-settings.js +118 -125
  81. package/lib/public/modules/home-shell.js +3 -0
  82. package/lib/public/modules/home-surface-boot.js +1 -9
  83. package/lib/public/modules/html-preview.js +19 -0
  84. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  85. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  86. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  87. package/lib/public/modules/knowledge-history-state.js +55 -0
  88. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  89. package/lib/public/modules/knowledge-preview-links.js +70 -0
  90. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  91. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  92. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  93. package/lib/public/modules/mate-knowledge.js +11 -3
  94. package/lib/public/modules/mate-management.js +54 -10
  95. package/lib/public/modules/mate-sidebar.js +11 -1
  96. package/lib/public/modules/mate-workspace-color.js +33 -0
  97. package/lib/public/modules/mention.js +5 -13
  98. package/lib/public/modules/profile.js +51 -18
  99. package/lib/public/modules/project-chat-identity.js +79 -0
  100. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  101. package/lib/public/modules/project-debate-workspace.js +185 -0
  102. package/lib/public/modules/project-header-identity.js +127 -0
  103. package/lib/public/modules/project-logs-render.js +9 -4
  104. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  105. package/lib/public/modules/project-mate-navigation.js +238 -0
  106. package/lib/public/modules/project-session-navigation.js +29 -0
  107. package/lib/public/modules/project-switcher.js +6 -8
  108. package/lib/public/modules/projects-hub-view.js +5 -6
  109. package/lib/public/modules/projects-hub.js +2 -6
  110. package/lib/public/modules/qrcode.js +27 -8
  111. package/lib/public/modules/search-clay-chat.js +8 -11
  112. package/lib/public/modules/session-activity-tooltip.js +12 -0
  113. package/lib/public/modules/session-create-form.js +86 -14
  114. package/lib/public/modules/session-create.js +50 -7
  115. package/lib/public/modules/session-list-visibility.js +41 -0
  116. package/lib/public/modules/session-presence-ui.js +4 -1
  117. package/lib/public/modules/session-title-marquee.js +51 -0
  118. package/lib/public/modules/sidebar-mates.js +3 -50
  119. package/lib/public/modules/sidebar-mobile.js +63 -60
  120. package/lib/public/modules/sidebar-projects.js +84 -97
  121. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  122. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  123. package/lib/public/modules/sidebar-sessions.js +49 -50
  124. package/lib/public/modules/sidebar.js +11 -13
  125. package/lib/public/modules/thinking-lifecycle.js +7 -6
  126. package/lib/public/modules/tool-palette-order.js +4 -3
  127. package/lib/public/modules/tool-palette.js +13 -3
  128. package/lib/public/modules/tools.js +22 -22
  129. package/lib/public/modules/worker-proposal.js +42 -8
  130. package/lib/public/modules/worker-runtime-picker.js +115 -0
  131. package/lib/public/modules/you-activity.js +121 -0
  132. package/lib/public/modules/you.js +251 -0
  133. package/lib/public/style.css +16 -8
  134. package/lib/public/vendor/codemirror/LICENSE +21 -0
  135. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  136. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  137. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  138. package/lib/public/vendor/codemirror/markdown.js +886 -0
  139. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  140. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  141. package/lib/sdk-bridge.js +15 -3
  142. package/lib/server-admin.js +11 -0
  143. package/lib/server-home-chat-events.js +1 -1
  144. package/lib/server-home-chat.js +25 -3
  145. package/lib/server-home-debate-planning.js +4 -1
  146. package/lib/server-home-debates.js +2 -0
  147. package/lib/server-home-mate-creation.js +4 -1
  148. package/lib/server-mate-instructions.js +44 -0
  149. package/lib/server-mates.js +12 -3
  150. package/lib/server-palette.js +1 -1
  151. package/lib/server-settings.js +65 -1
  152. package/lib/server-user-knowledge.js +119 -0
  153. package/lib/server.js +21 -1
  154. package/lib/session-visibility.js +7 -2
  155. package/lib/sessions.js +32 -6
  156. package/lib/user-knowledge-service.js +167 -0
  157. package/lib/users-permissions.js +7 -0
  158. package/lib/users-project-mate-filter-preferences.js +39 -0
  159. package/lib/users.js +6 -0
  160. package/lib/workspace-query-access.js +4 -1
  161. package/lib/ws-schema.js +50 -0
  162. package/package.json +10 -1
@@ -1,281 +1,90 @@
1
- // Vendor-aware Mate model chooser hosted by the centered Mate settings dialog.
2
-
1
+ // Correlated model catalogs and an explicit provider/model draft.
3
2
  import { getWs } from './ws-ref.js';
4
-
5
- var activeMateId = null;
6
- var activeMateName = "Mate";
7
- var state = null;
8
- var requestId = null;
9
- var selectionRequestId = null;
10
- var selectionModel = null;
11
- var activeSessionId = null;
12
- var requestSequence = 0;
13
-
14
- function nextRequestId(prefix) {
15
- requestSequence++;
16
- return prefix + "-" + Date.now() + "-" + requestSequence;
17
- }
18
-
19
- function send(message) {
20
- var ws = getWs();
21
- if (!ws || ws.readyState !== 1) return false;
22
- ws.send(JSON.stringify(message));
23
- return true;
24
- }
25
-
26
- function modelValue(entry) {
27
- if (!entry) return "";
28
- if (typeof entry === "string") return entry;
29
- return entry.value || entry.id || "";
30
- }
31
-
32
- function modelLabel(entry) {
33
- if (typeof entry === "string") return entry;
34
- return entry.displayName || entry.label || entry.name || modelValue(entry) || "Model";
35
- }
36
-
37
- function vendorLabel(vendor) {
38
- var vendors = state && state.vendors ? state.vendors : [];
39
- for (var i = 0; i < vendors.length; i++) {
40
- if (vendors[i] && vendors[i].id === vendor) return vendors[i].displayName || vendor;
41
- }
42
- return vendor || "Vendor";
43
- }
44
-
45
- function findFocusTarget(kind, value) {
46
- if (kind === "retry") return document.querySelector(".home-mate-settings-dialog .home-mate-model-retry");
47
- if (kind === "selection-status") return document.querySelector(".home-mate-settings-dialog .home-mate-model-selection-status");
48
- var selector = kind === "vendor" ? ".home-mate-settings-dialog [data-home-mate-vendor]" : ".home-mate-settings-dialog [data-home-mate-model]";
49
- var datasetKey = kind === "vendor" ? "homeMateVendor" : "homeMateModel";
50
- var options = document.querySelectorAll(selector);
51
- for (var i = 0; i < options.length; i++) {
52
- if (options[i].dataset[datasetKey] === value) return options[i];
3
+ import { store } from './store.js';
4
+ import { enhanceWorkerRuntimePicker } from './worker-runtime-picker.js';
5
+ var sequence = 0;
6
+ var timer = null;
7
+ function state() { return store.get('mateModelDraft'); }
8
+ function put(s) { store.set({mateModelDraft: s}); }
9
+ function value(entry) { return typeof entry === 'string' ? entry : entry.value || entry.id || ''; }
10
+ function label(entry) { return typeof entry === 'string' ? entry : entry.displayName || entry.label || entry.name || value(entry); }
11
+ function send(message) { var ws = getWs(); if (!ws || ws.readyState !== 1) return false; try { ws.send(JSON.stringify(message)); return true; } catch (error) { return false; } }
12
+ function id() { return 'mate-model-' + Date.now() + '-' + (++sequence); }
13
+ export function resetHomeMateModelPicker(mateId, name, mate, sessionId) {
14
+ clearTimeout(timer);
15
+ put({mateId: mateId, name: name, builtinClay: mate.builtinKey === 'clay', sessionId: sessionId || null, vendor: mate.vendor || '', model: mate.model || '', mateVendor: mate.vendor || '', mateModel: mate.model || '', vendors: [], models: [], status: 'idle', requestId: null, saveId: null, error: ''});
16
+ }
17
+ export function clearHomeMateModelPicker() { clearTimeout(timer); put(null); }
18
+ export function hasHomeMateModelDraft() { var s = state(); return !!s && (!!s.saveId || s.vendor !== s.mateVendor || s.model !== s.mateModel); }
19
+ function armTimeout(requestId, render) {
20
+ clearTimeout(timer);
21
+ timer = setTimeout(function () {
22
+ var s = state(); if (!s || (s.requestId !== requestId && s.saveId !== requestId)) return;
23
+ put(Object.assign({}, s, {requestId: null, saveId: null, status: s.status === 'loading' ? 'error' : s.status, error: 'No response received. Your choices are retained; try again.'})); render();
24
+ }, 15000);
25
+ }
26
+ export function requestHomeMateModels(vendor, render) {
27
+ var s = state(); if (!s || s.saveId) return;
28
+ var requestId = id();
29
+ put(Object.assign({}, s, {vendor: vendor || s.vendor, models: [], status: 'loading', requestId: requestId, error: ''}));
30
+ render();
31
+ if (!send({type: 'home_mate_models_get', mateId: s.mateId, vendor: vendor || s.vendor, requestId: requestId})) {
32
+ put(Object.assign({}, state(), {status: 'error', requestId: null, error: 'Clay is offline. Reconnect and try again.'})); render(); return;
53
33
  }
54
- return null;
55
- }
56
-
57
- function rerenderAndFocus(rerender, kind, value) {
58
- rerender();
59
- if (!kind) return;
60
- var target = findFocusTarget(kind, value);
61
- if (target) target.focus({ preventScroll: true });
62
- }
63
-
64
- export function resetHomeMateModelPicker(mateId, mateName, mate, sessionId) {
65
- activeMateId = mateId;
66
- activeMateName = mateName || "Mate";
67
- requestId = null;
68
- selectionRequestId = null;
69
- selectionModel = null;
70
- activeSessionId = sessionId || null;
71
- state = {
72
- status: "loading",
73
- vendor: mate && mate.vendor ? mate.vendor : "",
74
- model: mate && mate.model ? mate.model : "",
75
- mateVendor: mate && mate.vendor ? mate.vendor : "",
76
- mateModel: mate && mate.model ? mate.model : "",
77
- vendors: [],
78
- models: [],
79
- error: "",
80
- };
81
- }
82
-
83
- export function clearHomeMateModelPicker() {
84
- activeMateId = null;
85
- activeMateName = "Mate";
86
- state = null;
87
- requestId = null;
88
- selectionRequestId = null;
89
- selectionModel = null;
90
- activeSessionId = null;
91
- }
92
-
93
- export function requestHomeMateModels(vendor, rerender, focusAfter) {
94
- if (!activeMateId || !state || selectionRequestId) return;
95
- var requestedVendor = vendor || state.vendor || state.mateVendor;
96
- requestId = nextRequestId("home-mate-models");
97
- state.vendor = requestedVendor;
98
- state.status = "loading";
99
- state.models = [];
100
- state.error = "";
101
- rerenderAndFocus(rerender, focusAfter ? "vendor" : "", requestedVendor);
102
- if (send({ type: "home_mate_models_get", mateId: activeMateId, vendor: requestedVendor, requestId: requestId })) return;
103
- state.status = "error";
104
- state.error = "Clay is offline. Reconnect and try again.";
105
- rerenderAndFocus(rerender, "retry", "");
106
- }
107
-
108
- function selectMateModel(model, rerender) {
109
- if (!state || selectionRequestId || !model || !state.vendor) return;
110
- selectionRequestId = nextRequestId("home-mate-model-set");
111
- selectionModel = model;
112
- state.error = "";
113
- rerenderAndFocus(rerender, "selection-status", "");
114
- var message = { type: "home_mate_model_set", mateId: activeMateId, vendor: state.vendor, model: model, requestId: selectionRequestId };
115
- if (activeSessionId) message.sessionId = activeSessionId;
116
- if (send(message)) return;
117
- selectionRequestId = null;
118
- selectionModel = null;
119
- state.error = "Clay is offline. Reconnect and try again.";
120
- rerenderAndFocus(rerender, "model", model);
121
- }
122
-
123
- function appendHeading(body, text) {
124
- var heading = document.createElement("h3");
125
- heading.className = "home-mate-model-section-label";
126
- heading.textContent = text;
127
- body.appendChild(heading);
128
- }
129
-
130
- function renderVendors(body, rerender) {
131
- appendHeading(body, "Vendor");
132
- var list = document.createElement("div");
133
- list.className = "home-mate-vendor-list";
134
- list.setAttribute("role", "group");
135
- list.setAttribute("aria-label", "Vendor for " + activeMateName);
136
- var vendors = state.vendors || [];
137
- for (var i = 0; i < vendors.length; i++) {
138
- (function (vendor) {
139
- if (!vendor || !vendor.id) return;
140
- var button = document.createElement("button");
141
- button.type = "button";
142
- button.className = "home-mate-vendor-option";
143
- button.dataset.homeMateVendor = vendor.id;
144
- button.textContent = vendor.displayName || vendor.id;
145
- button.setAttribute("aria-pressed", vendor.id === state.vendor ? "true" : "false");
146
- button.disabled = !!selectionRequestId;
147
- if (vendor.id === state.mateVendor) button.title = "Current Mate vendor";
148
- button.addEventListener("click", function () {
149
- if (vendor.id !== state.vendor) requestHomeMateModels(vendor.id, rerender, true);
150
- });
151
- list.appendChild(button);
152
- })(vendors[i]);
153
- }
154
- if (!vendors.length) {
155
- var waiting = document.createElement("span");
156
- waiting.className = "home-mate-model-muted";
157
- waiting.textContent = "Loading available vendors…";
158
- list.appendChild(waiting);
159
- }
160
- body.appendChild(list);
161
- }
162
-
163
- function appendLoadState(body, rerender) {
164
- var loadState = document.createElement("div");
165
- loadState.className = "home-mate-model-state";
166
- if (state.status === "loading") {
167
- loadState.classList.add("home-mate-model-loading");
168
- loadState.setAttribute("role", "status");
169
- loadState.dataset.homeModelFocus = "catalog-status";
170
- loadState.tabIndex = -1;
171
- loadState.textContent = "Loading " + vendorLabel(state.vendor) + " models…";
172
- } else {
173
- if (state.status === "error") loadState.setAttribute("role", "alert");
174
- loadState.textContent = state.error || "No models are available for this vendor.";
175
- var retry = document.createElement("button");
176
- retry.type = "button";
177
- retry.className = "home-mate-model-retry";
178
- retry.dataset.homeModelFocus = "catalog-status";
179
- retry.textContent = "Try again";
180
- retry.addEventListener("click", function () { requestHomeMateModels(state.vendor, rerender, false); });
181
- loadState.appendChild(retry);
182
- }
183
- body.appendChild(loadState);
184
- }
185
-
186
- function renderModels(body, rerender) {
187
- appendHeading(body, "Model");
188
- if (state.status !== "ready") {
189
- appendLoadState(body, rerender);
190
- return;
191
- }
192
- var list = document.createElement("div");
193
- list.className = "home-mate-model-list";
194
- list.setAttribute("role", "group");
195
- list.setAttribute("aria-label", "Models from " + vendorLabel(state.vendor) + " for " + activeMateName);
196
- var models = state.models || [];
197
- for (var i = 0; i < models.length; i++) {
198
- (function (entry) {
199
- var value = modelValue(entry);
200
- if (!value) return;
201
- var button = document.createElement("button");
202
- button.type = "button";
203
- button.className = "home-mate-model-option";
204
- button.dataset.homeMateModel = value;
205
- button.setAttribute("aria-pressed", state.vendor === state.mateVendor && value === state.mateModel ? "true" : "false");
206
- button.disabled = !!selectionRequestId;
207
- var label = document.createElement("span");
208
- label.textContent = modelLabel(entry);
209
- var id = document.createElement("small");
210
- id.textContent = value;
211
- button.appendChild(label);
212
- button.appendChild(id);
213
- button.addEventListener("click", function () { selectMateModel(value, rerender); });
214
- list.appendChild(button);
215
- })(models[i]);
216
- }
217
- body.appendChild(list);
218
- var status = document.createElement("div");
219
- status.className = "home-mate-model-selection-status";
220
- status.setAttribute("aria-live", "polite");
221
- if (selectionRequestId) {
222
- status.dataset.homeMateModel = selectionModel;
223
- status.tabIndex = -1;
224
- }
225
- status.textContent = selectionRequestId ? "Saving vendor and model…" : (state.error || "");
226
- body.appendChild(status);
227
- }
228
-
229
- export function renderHomeMateModelPicker(body, rerender) {
230
- var note = document.createElement("p");
231
- note.className = "home-mate-model-note";
232
- note.textContent = activeSessionId ? "Used for this draft and future new conversations. Conversations with activity keep their committed vendor and model." : "Used for new conversations. Existing conversations keep their current vendor and model.";
34
+ armTimeout(requestId, render);
35
+ }
36
+ function save(render) {
37
+ var s = state(); if (!s || s.saveId || s.status !== 'ready' || !s.models.some(function (m) { return value(m) === s.model; })) return;
38
+ var saveId = id(); put(Object.assign({}, s, {saveId: saveId, error: '', notice: ''})); render();
39
+ var message = {type:'home_mate_model_set', mateId:s.mateId, vendor:s.vendor, model:s.model, requestId:saveId};
40
+ if (s.sessionId) message.sessionId = s.sessionId;
41
+ if (!send(message)) { put(Object.assign({}, state(), {saveId:null, error:'Clay is offline. Your choices are retained.'})); render(); return; }
42
+ armTimeout(saveId, render);
43
+ }
44
+ function selector(body, title, help, entries, selected, disabled, onChange) {
45
+ var row = document.createElement('div'); row.className = 'mate-settings-row';
46
+ var text = document.createElement('div'); var heading = document.createElement('label'); heading.textContent = title;
47
+ var hint = document.createElement('p'); hint.textContent = help; text.appendChild(heading); text.appendChild(hint); row.appendChild(text);
48
+ var control = document.createElement('div'); control.className = 'mate-settings-value';
49
+ var select = document.createElement('select'); select.className = 'worker-proposal-select'; select.setAttribute('aria-label', title); select.id = 'mate-settings-' + title.toLowerCase(); heading.htmlFor = select.id;
50
+ if (!entries.some(function (e) { return value(e) === selected; })) { var empty = document.createElement('option'); empty.value = selected; empty.textContent = selected || 'Choose a model'; empty.disabled = true; select.appendChild(empty); }
51
+ entries.forEach(function (entry) { var option = document.createElement('option'); option.value = value(entry); option.textContent = label(entry); select.appendChild(option); });
52
+ select.value = selected; select.disabled = disabled; select.addEventListener('change', function () { onChange(select.value); });
53
+ control.appendChild(select); row.appendChild(control); body.appendChild(row);
54
+ enhanceWorkerRuntimePicker(select, title, body.closest('.home-mate-settings-dialog'));
55
+ }
56
+ export function renderHomeMateModelPicker(body, render) {
57
+ var s = state(); if (!s) return;
58
+ var note = document.createElement('p'); note.className = 'mate-settings-help';
59
+ note.textContent = s.sessionId ? 'Used for this draft and future conversations. Conversations with activity keep their committed model.' : 'Used for new conversations with this Mate. Existing conversations keep their current model.';
60
+ if (s.builtinClay) note.textContent = 'Clay uses your Default AI for new conversations. This choice can update an eligible current draft; conversations with activity keep their committed model.';
233
61
  body.appendChild(note);
234
- if (!state) return;
235
- if (state.mateVendor && state.mateModel) {
236
- var current = document.createElement("p");
237
- current.className = "home-mate-model-current";
238
- current.textContent = "Current · " + vendorLabel(state.mateVendor) + " · " + state.mateModel;
239
- body.appendChild(current);
240
- }
241
- renderVendors(body, rerender);
242
- renderModels(body, rerender);
62
+ selector(body, 'Provider', 'Choose the service for this Mate.', s.vendors.map(function (v) { return {value:v.id, displayName:v.displayName || v.id}; }), s.vendor, !!s.saveId || !s.vendors.length, function (vendor) {
63
+ put(Object.assign({}, state(), {vendor:vendor, model:vendor === s.mateVendor ? s.mateModel : '', notice:''})); requestHomeMateModels(vendor, render);
64
+ });
65
+ selector(body, 'Model', 'Available models come from the selected provider.', s.models, s.model, !!s.saveId || s.status !== 'ready', function (model) { put(Object.assign({}, state(), {model:model, error:'', notice:''})); render(); });
66
+ var status = document.createElement('p'); status.className = 'mate-settings-status' + (s.error ? ' is-error' : ''); status.setAttribute('role', s.error ? 'alert' : 'status');
67
+ status.textContent = s.saveId ? 'Saving provider and model…' : s.status === 'loading' ? 'Loading models…' : s.error || s.notice || (s.status === 'empty' ? 'No models are available from this provider.' : ''); body.appendChild(status);
68
+ if (s.status === 'error' || s.status === 'empty') { var retry = document.createElement('button'); retry.type = 'button'; retry.className = 'mate-settings-button home-mate-model-retry'; retry.textContent = 'Try again'; retry.addEventListener('click', function () { requestHomeMateModels(s.vendor, render); }); body.appendChild(retry); }
69
+ if (!hasHomeMateModelDraft()) return;
70
+ var bar = document.createElement('div'); bar.className = 'mate-settings-savebar';
71
+ var cancel = document.createElement('button'); cancel.type = 'button'; cancel.className = 'mate-settings-button'; cancel.textContent = 'Cancel'; cancel.disabled = !!s.saveId;
72
+ cancel.addEventListener('click', function () { put(Object.assign({}, state(), {vendor:s.mateVendor, model:s.mateModel, error:'', notice:''})); requestHomeMateModels(s.mateVendor, render); });
73
+ var apply = document.createElement('button'); apply.type = 'button'; apply.className = 'mate-settings-button primary'; apply.textContent = 'Save model'; apply.disabled = !!s.saveId || s.status !== 'ready' || !s.models.some(function (m) { return value(m) === s.model; }); apply.addEventListener('click', function () { save(render); });
74
+ bar.appendChild(cancel); bar.appendChild(apply); body.appendChild(bar);
243
75
  }
244
-
245
76
  export function applyHomeMateModelsState(msg) {
246
- if (!state || msg.mateId !== activeMateId || msg.requestId !== requestId) return false;
247
- state.status = msg.status || "empty";
248
- state.vendor = msg.vendor || state.vendor;
249
- state.model = msg.model || "";
250
- state.mateVendor = msg.mateVendor || state.mateVendor;
251
- state.mateModel = msg.mateModel || state.mateModel;
252
- state.vendors = Array.isArray(msg.vendors) ? msg.vendors : state.vendors;
253
- state.models = Array.isArray(msg.models) ? msg.models : [];
254
- state.error = msg.error || "";
77
+ var s = state(); if (!s || msg.mateId !== s.mateId || msg.requestId !== s.requestId) return false;
78
+ clearTimeout(timer);
79
+ put(Object.assign({}, s, {requestId:null, status:msg.status || 'empty', vendor:msg.vendor || s.vendor, model:s.model || msg.model || '', mateVendor:msg.mateVendor || s.mateVendor, mateModel:msg.mateModel || s.mateModel, vendors:msg.vendors || s.vendors, models:msg.models || [], error:msg.error || ''}));
255
80
  return true;
256
81
  }
257
-
258
82
  export function applyHomeMateModelResult(msg) {
259
- if (!state || msg.mateId !== activeMateId || msg.requestId !== selectionRequestId) return false;
260
- selectionRequestId = null;
261
- selectionModel = null;
262
- if (!msg.ok) {
263
- state.error = msg.error || "Could not save this vendor and model.";
264
- return true;
265
- }
266
- state.mateVendor = msg.vendor || state.vendor;
267
- state.mateModel = msg.model || state.model;
268
- state.model = state.mateModel;
269
- state.error = msg.requestedSessionId && !msg.sessionApplied ? (msg.sessionReason || "Mate default updated. This conversation keeps its committed model.") : "";
270
- window.dispatchEvent(new CustomEvent("clay:home-mate-model-confirmed", { detail: {
271
- mateId: msg.mateId,
272
- vendor: state.mateVendor,
273
- model: state.mateModel,
274
- requestedSessionId: msg.requestedSessionId || null,
275
- sessionId: msg.sessionId || null,
276
- sessionApplied: msg.sessionApplied === true,
277
- sessionVendor: msg.sessionVendor || null,
278
- sessionModel: msg.sessionModel || null,
279
- } }));
83
+ var s = state(); if (!s || msg.mateId !== s.mateId || msg.requestId !== s.saveId) return false;
84
+ clearTimeout(timer);
85
+ if (!msg.ok) { put(Object.assign({}, s, {saveId:null, error:msg.error || 'Could not save. Your choices are retained.'})); return true; }
86
+ var vendor = msg.vendor || s.vendor; var model = msg.model || s.model;
87
+ put(Object.assign({}, s, {saveId:null, vendor:vendor, model:model, mateVendor:vendor, mateModel:model, error:'', notice:msg.requestedSessionId && !msg.sessionApplied ? (msg.sessionReason || 'Default saved. This conversation keeps its committed model.') : 'Model saved.'}));
88
+ window.dispatchEvent(new CustomEvent('clay:home-mate-model-confirmed', {detail:{mateId:msg.mateId, vendor:vendor, model:model, requestedSessionId:msg.requestedSessionId || null, sessionId:msg.sessionId || null, sessionApplied:msg.sessionApplied === true, sessionVendor:msg.sessionVendor || null, sessionModel:msg.sessionModel || null}}));
280
89
  return true;
281
90
  }
@@ -0,0 +1,72 @@
1
+ import { mateAvatarUrl } from './avatar.js';
2
+ import { store } from './store.js';
3
+ import { getWs } from './ws-ref.js';
4
+ var timer = null;
5
+ var sequence = 0;
6
+ function state() { return store.get('mateProfileDraft'); }
7
+ function put(s) { store.set({mateProfileDraft:s}); }
8
+ function name(mate) { return (mate.profile || {}).displayName || mate.name || 'Mate'; }
9
+ export function resetMateProfile() { clearTimeout(timer); put(null); }
10
+ export function hasMateProfileDraft() { var s = state(); return !!s && (!!s.requestId || s.name !== s.savedName || s.bio !== s.savedBio); }
11
+ export function mateProfileSaving() { var s = state(); return !!s && !!s.requestId; }
12
+ export function applyMateProfile(msg) {
13
+ var s = state();
14
+ if (!s || !s.requestId || msg.requestId !== s.requestId || (msg.mate ? msg.mate.id : msg.mateId) !== s.mateId) return false;
15
+ clearTimeout(timer);
16
+ if (msg.mate) put(null);
17
+ else put(Object.assign({},s,{requestId:null,error:msg.error || 'Could not save. Your changes are retained.'}));
18
+ return true;
19
+ }
20
+ export function renderMateProfile(body, mate, render, editAvatar) {
21
+ var section = document.createElement('section'); section.className = 'mate-settings-profile';
22
+ var heading = document.createElement('div'); heading.className = 'mate-settings-profile-heading';
23
+ var title = document.createElement('h4'); title.textContent = name(mate); heading.appendChild(title);
24
+ var avatar = document.createElement('button'); avatar.type = 'button'; avatar.className = 'mate-settings-profile-avatar';
25
+ avatar.id = 'mate-profile-avatar'; avatar.setAttribute('aria-label','Change Mate avatar'); avatar.title = 'Change avatar';
26
+ avatar.disabled = !!(mate.profile || {}).avatarLocked;
27
+ avatar.setAttribute('aria-expanded',String(!!store.get('mateSettingsAvatarOpen')));
28
+ var image = document.createElement('img'); image.src = mateAvatarUrl(mate,72); image.alt = ''; avatar.appendChild(image);
29
+ avatar.addEventListener('click',editAvatar); section.appendChild(avatar);
30
+ section.appendChild(heading); body.appendChild(section);
31
+ var s = state();
32
+ function button(label, action, primary, parent) {
33
+ var b = document.createElement('button'); b.type = 'button'; b.textContent = label;
34
+ b.className = 'mate-settings-button' + (primary ? ' primary' : ''); b.disabled = !!s && !!s.requestId;
35
+ b.addEventListener('click',action); (parent || section).appendChild(b); return b;
36
+ }
37
+ if (!s || s.mateId !== mate.id) {
38
+ if (!mate.primary) {
39
+ var edit = button('Edit',function () {
40
+ put({mateId:mate.id,name:name(mate),bio:mate.bio || '',savedName:name(mate),savedBio:mate.bio || '',error:'',requestId:null});
41
+ render(); document.getElementById('mate-profile-name').focus();
42
+ },false,heading); edit.id = 'mate-profile-edit'; edit.setAttribute('aria-label','Edit Mate identity');
43
+ }
44
+
45
+ var bio = document.createElement('p'); bio.className = 'mate-settings-help'; bio.textContent = mate.bio || 'No introduction yet.'; section.appendChild(bio);
46
+ if (mate.primary) { var note = document.createElement('p'); note.className = 'mate-settings-help'; note.textContent = 'Clay manages this built-in identity.'; section.appendChild(note); }
47
+ return;
48
+ }
49
+ section.classList.add('is-editing');
50
+ function field(title, key, multiline) {
51
+ var label = document.createElement('label'); label.className = 'mate-settings-profile-field'; label.textContent = title;
52
+ var input = document.createElement(multiline ? 'textarea' : 'input'); input.id = 'mate-profile-' + key; input.value = s[key]; input.disabled = !!s.requestId;
53
+ input.addEventListener('input',function () { put(Object.assign({},state(),{[key]:input.value})); });
54
+ label.appendChild(input); section.appendChild(label);
55
+ }
56
+ field('Name','name',false); field('Introduction','bio',true);
57
+ var status = document.createElement('p'); status.className = 'mate-settings-status' + (s.error ? ' is-error' : ''); status.setAttribute('role',s.error ? 'alert' : 'status'); status.textContent = s.error || (s.requestId ? 'Saving identity…' : ''); section.appendChild(status);
58
+ var bar = document.createElement('div'); bar.className = 'mate-settings-savebar'; section.appendChild(bar);
59
+ button('Cancel',function () { resetMateProfile(); render(); document.getElementById('mate-profile-edit').focus(); },false,bar);
60
+ button('Save identity',function () {
61
+ var draft = state(); var ws = getWs();
62
+ if (!draft.name.trim()) { put(Object.assign({},draft,{error:'Enter a name.'})); render(); return; }
63
+ if (!ws || ws.readyState !== 1) { put(Object.assign({},draft,{error:'Clay is offline. Your changes are retained.'})); render(); return; }
64
+ var requestId = 'mate-profile-' + Date.now() + '-' + (++sequence);
65
+ put(Object.assign({},draft,{requestId:requestId,error:''})); render();
66
+ try {
67
+ var latest = (store.get('cachedMatesList') || []).find(function (m) { return m.id === mate.id; }) || mate;
68
+ ws.send(JSON.stringify({type:'mate_update',mateId:mate.id,requestId:requestId,updates:{name:draft.name.trim(),bio:draft.bio.trim(),profile:Object.assign({},latest.profile,{displayName:draft.name.trim()})}}));
69
+ } catch (error) { put(Object.assign({},draft,{requestId:null,error:'Could not send. Your changes are retained.'})); render(); return; }
70
+ clearTimeout(timer); timer = setTimeout(function () { if (state() && state().requestId === requestId) { put(Object.assign({},state(),{requestId:null,error:'No response received. Your changes are retained; try again.'})); render(); } },15000);
71
+ },true,bar);
72
+ }