clay-server 5.1.0-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 (157) 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-knowledge.js +315 -136
  22. package/lib/project-mate-creation-proposal.js +1 -0
  23. package/lib/project-sessions.js +39 -2
  24. package/lib/project-user-knowledge.js +60 -0
  25. package/lib/project.js +59 -18
  26. package/lib/public/app.js +73 -34
  27. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  28. package/lib/public/css/clay-workspace.css +105 -0
  29. package/lib/public/css/debates-workbench.css +47 -0
  30. package/lib/public/css/home-debate-live.css +26 -0
  31. package/lib/public/css/home-hub.css +3 -1
  32. package/lib/public/css/home-mate-model-picker.css +15 -131
  33. package/lib/public/css/home-mate-settings.css +168 -16
  34. package/lib/public/css/icon-strip.css +249 -32
  35. package/lib/public/css/mate-conversation.css +176 -0
  36. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  37. package/lib/public/css/mate-workspace-color.css +16 -0
  38. package/lib/public/css/mates.css +28 -28
  39. package/lib/public/css/messages.css +1 -114
  40. package/lib/public/css/mobile-nav.css +16 -16
  41. package/lib/public/css/overlays.css +8 -0
  42. package/lib/public/css/project-logs.css +7 -0
  43. package/lib/public/css/project-mate-creation.css +105 -0
  44. package/lib/public/css/right-workbench.css +3 -3
  45. package/lib/public/css/session-folders.css +80 -21
  46. package/lib/public/css/sidebar.css +183 -107
  47. package/lib/public/css/title-bar.css +151 -0
  48. package/lib/public/css/worker-delegation.css +47 -0
  49. package/lib/public/css/worker-proposal.css +72 -150
  50. package/lib/public/css/you.css +74 -0
  51. package/lib/public/index.html +33 -101
  52. package/lib/public/modules/account-island.js +230 -0
  53. package/lib/public/modules/app-cursors.js +19 -43
  54. package/lib/public/modules/app-debate-ui.js +4 -3
  55. package/lib/public/modules/app-header.js +106 -0
  56. package/lib/public/modules/app-message-router.js +12 -3
  57. package/lib/public/modules/app-messages.js +34 -9
  58. package/lib/public/modules/app-notifications.js +4 -7
  59. package/lib/public/modules/app-projects.js +5 -38
  60. package/lib/public/modules/app-rendering.js +12 -13
  61. package/lib/public/modules/avatar.js +25 -9
  62. package/lib/public/modules/chat-render-runtime.js +16 -4
  63. package/lib/public/modules/command-palette.js +2 -15
  64. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  65. package/lib/public/modules/debate-controls.js +299 -0
  66. package/lib/public/modules/debate.js +2 -0
  67. package/lib/public/modules/debates-workbench.js +339 -0
  68. package/lib/public/modules/default-ai.js +81 -65
  69. package/lib/public/modules/home-debate-controls.js +18 -289
  70. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  71. package/lib/public/modules/home-mate-color-picker.js +66 -0
  72. package/lib/public/modules/home-mate-creation.js +1 -1
  73. package/lib/public/modules/home-mate-instructions.js +90 -0
  74. package/lib/public/modules/home-mate-model-picker.js +80 -271
  75. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  76. package/lib/public/modules/home-mate-settings.js +118 -125
  77. package/lib/public/modules/home-shell.js +3 -0
  78. package/lib/public/modules/home-surface-boot.js +1 -9
  79. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  80. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  81. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  82. package/lib/public/modules/knowledge-history-state.js +55 -0
  83. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  84. package/lib/public/modules/knowledge-preview-links.js +70 -0
  85. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  86. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  87. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  88. package/lib/public/modules/mate-knowledge.js +11 -3
  89. package/lib/public/modules/mate-management.js +54 -10
  90. package/lib/public/modules/mate-sidebar.js +11 -1
  91. package/lib/public/modules/mate-workspace-color.js +33 -0
  92. package/lib/public/modules/mention.js +5 -13
  93. package/lib/public/modules/profile.js +51 -18
  94. package/lib/public/modules/project-chat-identity.js +79 -0
  95. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  96. package/lib/public/modules/project-debate-workspace.js +185 -0
  97. package/lib/public/modules/project-header-identity.js +127 -0
  98. package/lib/public/modules/project-logs-render.js +9 -4
  99. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  100. package/lib/public/modules/project-mate-navigation.js +238 -0
  101. package/lib/public/modules/project-session-navigation.js +29 -0
  102. package/lib/public/modules/project-switcher.js +6 -8
  103. package/lib/public/modules/projects-hub-view.js +5 -6
  104. package/lib/public/modules/projects-hub.js +2 -6
  105. package/lib/public/modules/qrcode.js +27 -8
  106. package/lib/public/modules/search-clay-chat.js +8 -11
  107. package/lib/public/modules/session-activity-tooltip.js +12 -0
  108. package/lib/public/modules/session-create-form.js +86 -14
  109. package/lib/public/modules/session-create.js +50 -7
  110. package/lib/public/modules/session-list-visibility.js +41 -0
  111. package/lib/public/modules/session-presence-ui.js +4 -1
  112. package/lib/public/modules/session-title-marquee.js +51 -0
  113. package/lib/public/modules/sidebar-mates.js +3 -50
  114. package/lib/public/modules/sidebar-mobile.js +63 -60
  115. package/lib/public/modules/sidebar-projects.js +84 -97
  116. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  117. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  118. package/lib/public/modules/sidebar-sessions.js +49 -50
  119. package/lib/public/modules/sidebar.js +11 -13
  120. package/lib/public/modules/thinking-lifecycle.js +7 -6
  121. package/lib/public/modules/tool-palette-order.js +4 -3
  122. package/lib/public/modules/tool-palette.js +13 -3
  123. package/lib/public/modules/tools.js +22 -22
  124. package/lib/public/modules/worker-proposal.js +42 -8
  125. package/lib/public/modules/worker-runtime-picker.js +115 -0
  126. package/lib/public/modules/you-activity.js +121 -0
  127. package/lib/public/modules/you.js +251 -0
  128. package/lib/public/style.css +16 -8
  129. package/lib/public/vendor/codemirror/LICENSE +21 -0
  130. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  131. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  132. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  133. package/lib/public/vendor/codemirror/markdown.js +886 -0
  134. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  135. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  136. package/lib/sdk-bridge.js +15 -3
  137. package/lib/server-admin.js +11 -0
  138. package/lib/server-home-chat-events.js +1 -1
  139. package/lib/server-home-chat.js +25 -3
  140. package/lib/server-home-debate-planning.js +4 -1
  141. package/lib/server-home-debates.js +2 -0
  142. package/lib/server-home-mate-creation.js +4 -1
  143. package/lib/server-mate-instructions.js +44 -0
  144. package/lib/server-mates.js +12 -3
  145. package/lib/server-palette.js +1 -1
  146. package/lib/server-settings.js +65 -1
  147. package/lib/server-user-knowledge.js +119 -0
  148. package/lib/server.js +21 -1
  149. package/lib/session-visibility.js +7 -2
  150. package/lib/sessions.js +32 -6
  151. package/lib/user-knowledge-service.js +167 -0
  152. package/lib/users-permissions.js +7 -0
  153. package/lib/users-project-mate-filter-preferences.js +39 -0
  154. package/lib/users.js +6 -0
  155. package/lib/workspace-query-access.js +4 -1
  156. package/lib/ws-schema.js +50 -0
  157. package/package.json +10 -1
@@ -0,0 +1,90 @@
1
+ import { store } from './store.js';
2
+ import { getWs } from './ws-ref.js';
3
+
4
+ var sequence = 0;
5
+ var timer = null;
6
+ function state() { return store.get('mateInstructionsDraft'); }
7
+ function put(value) { store.set({mateInstructionsDraft: value}); }
8
+ export function resetMateInstructions(mateId) {
9
+ clearTimeout(timer);
10
+ put(mateId ? {mateId: mateId, content: '', saved: '', revision: '', status: 'idle', error: '', requestId: null} : null);
11
+ }
12
+ export function hasMateInstructionDraft() { var s = state(); return !!s && (s.content !== s.saved || s.status === 'saving'); }
13
+ function request(save, render) {
14
+ var s = state();
15
+ if (!s || s.status === 'saving') return;
16
+ var id = 'mate-instructions-' + Date.now() + '-' + (++sequence);
17
+ var ws = getWs();
18
+ if (!ws || ws.readyState !== 1) { put(Object.assign({}, s, {error: 'Clay is offline. Reconnect and try again.', status: s.revision ? 'ready' : 'error'})); render(); return; }
19
+ put(Object.assign({}, s, {requestId: id, status: save ? 'saving' : 'loading', error: ''}));
20
+ render();
21
+ try { ws.send(JSON.stringify({type: save ? 'mate_instructions_set' : 'mate_instructions_get', mateId: s.mateId, requestId: id, content: save ? s.content : undefined, revision: save ? s.revision : undefined})); }
22
+ catch (error) { put(Object.assign({}, state(), {status: s.revision ? 'ready' : 'error', requestId: null, error: 'Could not send. Your draft is retained.'})); render(); return; }
23
+ clearTimeout(timer);
24
+ timer = setTimeout(function () {
25
+ var current = state();
26
+ if (!current || current.requestId !== id) return;
27
+ put(Object.assign({}, current, {requestId: null, status: current.revision ? 'ready' : 'error', error: 'No response received. Retry to check the saved prompt.'}));
28
+ render();
29
+ }, 15000);
30
+ }
31
+ export function loadMateInstructions(render) { request(false, render); }
32
+ export function applyMateInstructions(msg) {
33
+ var s = state();
34
+ if (!s || msg.mateId !== s.mateId || msg.requestId !== s.requestId) return false;
35
+ clearTimeout(timer);
36
+ if (!msg.ok) put(Object.assign({}, s, {status: s.revision ? 'ready' : 'error', requestId: null, conflict: !!msg.conflict, error: msg.error || 'Could not save the prompt.'}));
37
+ else put(Object.assign({}, s, {status: 'ready', requestId: null, content: msg.content, saved: msg.content, revision: msg.revision, conflict: false, error: '', notice: msg.operation === 'save' ? 'Prompt saved. New conversations use these instructions.' : ''}));
38
+ return true;
39
+ }
40
+ export function renderMateInstructions(body, render) {
41
+ var s = state();
42
+ if (!s) return;
43
+ var section = document.createElement('section');
44
+ section.className = 'mate-instructions';
45
+ section.innerHTML = '<p class="mate-settings-help">The identity and instructions this Mate uses. Clay manages its system capabilities separately.</p>';
46
+ body.appendChild(section);
47
+ if (s.status === 'idle' || (s.status === 'loading' && !s.revision)) {
48
+ section.insertAdjacentHTML('beforeend', '<p role="status" class="mate-settings-help">Loading prompt…</p>');
49
+ return;
50
+ }
51
+ if (s.revision) {
52
+ var input = document.createElement('textarea');
53
+ input.className = 'mate-instructions-input';
54
+ input.id = 'mate-prompt-input';
55
+ input.setAttribute('aria-label', 'Mate prompt');
56
+ input.spellcheck = false;
57
+ input.value = s.content;
58
+ input.disabled = s.status === 'saving' || s.status === 'loading';
59
+ section.appendChild(input);
60
+ input.addEventListener('input', function () { put(Object.assign({}, state(), {content: input.value, notice: ''})); updateActions(); });
61
+ }
62
+ var status = document.createElement('p');
63
+ status.className = 'mate-settings-status' + (s.error ? ' is-error' : '');
64
+ status.setAttribute('role', s.error ? 'alert' : 'status');
65
+ status.textContent = s.error || s.notice || (s.status === 'saving' ? 'Saving prompt…' : '');
66
+ section.appendChild(status);
67
+ var actions = document.createElement('div');
68
+ actions.className = 'mate-settings-savebar';
69
+ section.appendChild(actions);
70
+ function button(text, action, primary) {
71
+ var el = document.createElement('button'); el.type = 'button'; el.textContent = text; el.className = 'mate-settings-button' + (primary ? ' primary' : ''); el.addEventListener('click', action); actions.appendChild(el); return el;
72
+ }
73
+ function updateActions() {
74
+ var current = state(); actions.replaceChildren();
75
+ if (current.conflict || !current.revision) {
76
+ button(current.conflict ? 'Reload saved prompt' : 'Try again', function () { request(false, render); });
77
+ if (current.conflict) {
78
+ var help = document.createElement('span'); help.textContent = 'Copy your draft before reloading.'; help.className = 'mate-settings-help'; actions.appendChild(help);
79
+ }
80
+ return;
81
+ }
82
+ actions.hidden = current.content === current.saved && current.status !== 'saving';
83
+ if (actions.hidden) return;
84
+ var cancel = button('Cancel', function () { put(Object.assign({}, current, {content: current.saved, error: '', notice: ''})); render(); });
85
+ var save = button('Save prompt', function () { request(true, render); }, true);
86
+ cancel.disabled = current.status === 'saving';
87
+ save.disabled = current.status === 'saving';
88
+ }
89
+ updateActions();
90
+ }
@@ -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
+ }