@chrysb/alphaclaw 0.5.6 → 0.6.0-beta.0

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 (84) hide show
  1. package/bin/alphaclaw.js +6 -1
  2. package/lib/public/css/agents.css +92 -0
  3. package/lib/public/css/explorer.css +101 -0
  4. package/lib/public/css/shell.css +15 -4
  5. package/lib/public/js/app.js +69 -3
  6. package/lib/public/js/components/action-button.js +5 -0
  7. package/lib/public/js/components/agents-tab/agent-bindings-section/helpers.js +76 -0
  8. package/lib/public/js/components/agents-tab/agent-bindings-section/index.js +490 -0
  9. package/lib/public/js/components/agents-tab/agent-bindings-section/use-agent-bindings.js +256 -0
  10. package/lib/public/js/components/agents-tab/agent-detail-panel.js +74 -0
  11. package/lib/public/js/components/agents-tab/agent-identity-section.js +175 -0
  12. package/lib/public/js/components/agents-tab/agent-overview/index.js +53 -0
  13. package/lib/public/js/components/agents-tab/agent-overview/manage-card.js +44 -0
  14. package/lib/public/js/components/agents-tab/agent-overview/model-card.js +158 -0
  15. package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +169 -0
  16. package/lib/public/js/components/agents-tab/agent-overview/use-workspace-card.js +45 -0
  17. package/lib/public/js/components/agents-tab/agent-overview/workspace-card.js +47 -0
  18. package/lib/public/js/components/agents-tab/agent-pairing-section.js +265 -0
  19. package/lib/public/js/components/agents-tab/create-agent-modal.js +189 -0
  20. package/lib/public/js/components/agents-tab/create-channel-modal.js +323 -0
  21. package/lib/public/js/components/agents-tab/delete-agent-dialog.js +50 -0
  22. package/lib/public/js/components/agents-tab/edit-agent-modal.js +109 -0
  23. package/lib/public/js/components/agents-tab/index.js +148 -0
  24. package/lib/public/js/components/agents-tab/use-agents.js +89 -0
  25. package/lib/public/js/components/channel-account-status-badge.js +35 -0
  26. package/lib/public/js/components/channel-operations-panel.js +33 -0
  27. package/lib/public/js/components/channels.js +545 -60
  28. package/lib/public/js/components/envars.js +25 -4
  29. package/lib/public/js/components/general/index.js +21 -11
  30. package/lib/public/js/components/general/use-general-tab.js +78 -16
  31. package/lib/public/js/components/google/gmail-setup-wizard.js +1 -3
  32. package/lib/public/js/components/google/index.js +28 -30
  33. package/lib/public/js/components/icons.js +37 -0
  34. package/lib/public/js/components/models-tab/index.js +58 -224
  35. package/lib/public/js/components/models-tab/model-picker.js +212 -0
  36. package/lib/public/js/components/models-tab/use-models.js +17 -14
  37. package/lib/public/js/components/onboarding/use-welcome-pairing.js +4 -4
  38. package/lib/public/js/components/onboarding/welcome-pairing-step.js +2 -2
  39. package/lib/public/js/components/overflow-menu.js +122 -0
  40. package/lib/public/js/components/pairings.js +36 -8
  41. package/lib/public/js/components/routes/agents-route.js +27 -0
  42. package/lib/public/js/components/routes/general-route.js +2 -0
  43. package/lib/public/js/components/routes/index.js +1 -0
  44. package/lib/public/js/components/routes/telegram-route.js +2 -2
  45. package/lib/public/js/components/secret-input.js +8 -1
  46. package/lib/public/js/components/sidebar.js +64 -26
  47. package/lib/public/js/components/telegram-workspace/index.js +175 -74
  48. package/lib/public/js/components/telegram-workspace/manage.js +83 -10
  49. package/lib/public/js/components/telegram-workspace/onboarding.js +9 -8
  50. package/lib/public/js/components/webhooks.js +43 -18
  51. package/lib/public/js/hooks/use-app-shell-controller.js +7 -0
  52. package/lib/public/js/hooks/use-browse-navigation.js +8 -5
  53. package/lib/public/js/hooks/use-destination-session-selection.js +8 -1
  54. package/lib/public/js/lib/api.js +163 -9
  55. package/lib/public/js/lib/app-navigation.js +2 -1
  56. package/lib/public/js/lib/channel-create-operation.js +102 -0
  57. package/lib/public/js/lib/format.js +14 -0
  58. package/lib/public/js/lib/sse.js +51 -0
  59. package/lib/public/js/lib/telegram-api.js +38 -18
  60. package/lib/public/setup.html +1 -0
  61. package/lib/public/shared/browse-file-policies.json +0 -1
  62. package/lib/server/agents/service.js +1478 -0
  63. package/lib/server/constants.js +2 -2
  64. package/lib/server/env.js +3 -1
  65. package/lib/server/gateway.js +104 -20
  66. package/lib/server/gmail-watch.js +29 -2
  67. package/lib/server/onboarding/import/import-applier.js +0 -1
  68. package/lib/server/onboarding/index.js +0 -6
  69. package/lib/server/onboarding/workspace.js +73 -38
  70. package/lib/server/openclaw-config.js +23 -0
  71. package/lib/server/operation-events.js +141 -0
  72. package/lib/server/routes/agents.js +266 -0
  73. package/lib/server/routes/pairings.js +135 -25
  74. package/lib/server/routes/system.js +90 -10
  75. package/lib/server/routes/telegram.js +247 -51
  76. package/lib/server/telegram-workspace.js +61 -10
  77. package/lib/server/topic-registry.js +66 -7
  78. package/lib/server/watchdog.js +39 -1
  79. package/lib/server/webhooks.js +60 -12
  80. package/lib/server.js +21 -7
  81. package/lib/setup/core-prompts/AGENTS.md +6 -5
  82. package/lib/setup/core-prompts/TOOLS.md +1 -8
  83. package/package.json +1 -1
  84. package/lib/setup/skills/control-ui/SKILL.md +0 -62
@@ -45,6 +45,7 @@ const kFeatureIconByName = {
45
45
  },
46
46
  };
47
47
  const normalizeEnvVarKey = (raw) => raw.trim().toUpperCase().replace(/[^A-Z0-9_]/g, "_");
48
+ const kManagedChannelTokenPattern = /^(TELEGRAM|DISCORD)_BOT_TOKEN(?:_[A-Z0-9_]+)?$/;
48
49
  const stripSurroundingQuotes = (raw) => {
49
50
  const value = String(raw || "").trim();
50
51
  if (value.length < 2) return value;
@@ -56,6 +57,8 @@ const stripSurroundingQuotes = (raw) => {
56
57
  if (startsWithSingle && endsWithSingle) return value.slice(1, -1);
57
58
  return value;
58
59
  };
60
+ const isManagedChannelTokenKey = (key = "") =>
61
+ kManagedChannelTokenPattern.test(String(key || "").trim().toUpperCase());
59
62
  const getVarsSignature = (items) =>
60
63
  JSON.stringify(
61
64
  (items || [])
@@ -234,7 +237,7 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
234
237
  setSaving(true);
235
238
  try {
236
239
  const toSave = vars
237
- .filter((v) => v.editable)
240
+ .filter((v) => v.editable && !isManagedChannelTokenKey(v?.key))
238
241
  .map((v) => ({ key: v.key, value: v.value }));
239
242
  const result = await saveEnvVars(toSave);
240
243
  const needsRestart = !!result?.restartRequired;
@@ -281,12 +284,17 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
281
284
  const addVars = (pairs) => {
282
285
  let added = 0;
283
286
  const blocked = [];
287
+ const managedChannelKeys = [];
284
288
  const addedCustomKeys = [];
285
289
  setVars((prev) => {
286
290
  const next = [...prev];
287
291
  for (const { key: rawKey, value } of pairs) {
288
292
  const key = normalizeEnvVarKey(rawKey);
289
293
  if (!key) continue;
294
+ if (isManagedChannelTokenKey(key)) {
295
+ managedChannelKeys.push(key);
296
+ continue;
297
+ }
290
298
  if (reservedKeys.has(key)) {
291
299
  blocked.push(key);
292
300
  continue;
@@ -313,7 +321,7 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
313
321
  if (addedCustomKeys.length) {
314
322
  setPendingCustomKeys((prev) => [...prev, ...addedCustomKeys]);
315
323
  }
316
- return { added, blocked };
324
+ return { added, blocked, managedChannelKeys };
317
325
  };
318
326
 
319
327
  const handlePaste = (e, fallbackField) => {
@@ -321,7 +329,7 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
321
329
  const pairs = parsePaste(text);
322
330
  if (pairs.length > 1) {
323
331
  e.preventDefault();
324
- const { added, blocked } = addVars(pairs);
332
+ const { added, blocked, managedChannelKeys } = addVars(pairs);
325
333
  setNewKey("");
326
334
  setNewVal("");
327
335
  if (blocked.length) {
@@ -331,6 +339,13 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
331
339
  "error",
332
340
  );
333
341
  }
342
+ if (managedChannelKeys.length) {
343
+ const uniqueManagedKeys = Array.from(new Set(managedChannelKeys));
344
+ showToast(
345
+ `Channel bot tokens are managed from Channels: ${uniqueManagedKeys.join(", ")}`,
346
+ "error",
347
+ );
348
+ }
334
349
  if (added) {
335
350
  showToast(`Added ${added} variable${added !== 1 ? "s" : ""}`, "success");
336
351
  }
@@ -367,6 +382,10 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
367
382
  const handleAddVar = () => {
368
383
  const key = normalizeEnvVarKey(newKey);
369
384
  if (!key) return;
385
+ if (isManagedChannelTokenKey(key)) {
386
+ showToast(`Channel bot tokens are managed from Channels: ${key}`, "error");
387
+ return;
388
+ }
370
389
  if (reservedKeys.has(key)) {
371
390
  showToast(`Reserved var can't be added: ${key}`, "error");
372
391
  return;
@@ -376,9 +395,11 @@ export const Envars = ({ onRestartRequired = () => {} }) => {
376
395
  setNewVal("");
377
396
  };
378
397
 
398
+ const visibleVars = vars.filter((v) => !isManagedChannelTokenKey(v?.key));
399
+
379
400
  // Group vars
380
401
  const grouped = {};
381
- for (const v of vars) {
402
+ for (const v of visibleVars) {
382
403
  const g = v.group || "custom";
383
404
  if (!grouped[g]) grouped[g] = [];
384
405
  grouped[g].push(v);
@@ -2,6 +2,7 @@ import { h } from "https://esm.sh/preact";
2
2
  import htm from "https://esm.sh/htm";
3
3
  import { Gateway } from "../gateway.js";
4
4
  import { Channels } from "../channels.js";
5
+ import { ChannelOperationsPanel } from "../channel-operations-panel.js";
5
6
  import { Pairings } from "../pairings.js";
6
7
  import { DevicePairings } from "../device-pairings.js";
7
8
  import { Google } from "../google/index.js";
@@ -17,6 +18,7 @@ export const GeneralTab = ({
17
18
  statusData = null,
18
19
  watchdogData = null,
19
20
  doctorStatusData = null,
21
+ agents = [],
20
22
  doctorWarningDismissedUntilMs = 0,
21
23
  onRefreshStatuses = () => {},
22
24
  onSwitchTab = () => {},
@@ -62,17 +64,25 @@ export const GeneralTab = ({
62
64
  onOpenDoctor=${() => onSwitchTab("doctor")}
63
65
  onDismiss=${onDismissDoctorWarning}
64
66
  />
65
- <${Channels}
66
- channels=${state.channels}
67
- onSwitchTab=${onSwitchTab}
68
- onNavigate=${onNavigate}
69
- />
70
- <${Pairings}
71
- pending=${state.pending}
72
- channels=${state.channels}
73
- visible=${state.hasUnpaired}
74
- onApprove=${actions.handleApprove}
75
- onReject=${actions.handleReject}
67
+ <${ChannelOperationsPanel}
68
+ channelsSection=${html`
69
+ <${Channels}
70
+ channels=${state.channels}
71
+ agents=${agents}
72
+ onNavigate=${onNavigate}
73
+ onRefreshStatuses=${onRefreshStatuses}
74
+ />
75
+ `}
76
+ pairingsSection=${html`
77
+ <${Pairings}
78
+ pending=${state.pending}
79
+ channels=${state.channels}
80
+ visible=${state.hasUnpaired}
81
+ statusRefreshing=${state.pairingStatusRefreshing}
82
+ onApprove=${actions.handleApprove}
83
+ onReject=${actions.handleReject}
84
+ />
85
+ `}
76
86
  />
77
87
  <${Features} onSwitchTab=${onSwitchTab} />
78
88
  <${Google}
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from "https://esm.sh/preact/hooks";
1
+ import { useEffect, useRef, useState } from "https://esm.sh/preact/hooks";
2
2
  import {
3
3
  approveDevice,
4
4
  approvePairing,
@@ -30,6 +30,10 @@ export const useGeneralTab = ({
30
30
  const [syncCronSchedule, setSyncCronSchedule] = useState(kDefaultSyncCronSchedule);
31
31
  const [savingSyncCron, setSavingSyncCron] = useState(false);
32
32
  const [syncCronChoice, setSyncCronChoice] = useState(kDefaultSyncCronSchedule);
33
+ const [pairingStatusRefreshing, setPairingStatusRefreshing] = useState(false);
34
+ const [devicePollingEnabled, setDevicePollingEnabled] = useState(false);
35
+ const [cliAutoApproveComplete, setCliAutoApproveComplete] = useState(false);
36
+ const pairingRefreshTimerRef = useRef(null);
33
37
 
34
38
  const status = statusData;
35
39
  const watchdogStatus = watchdogData;
@@ -42,7 +46,15 @@ export const useGeneralTab = ({
42
46
 
43
47
  const hasUnpaired = ALL_CHANNELS.some((channel) => {
44
48
  const info = channels?.[channel];
45
- return info && info.status !== "paired";
49
+ if (!info) return false;
50
+ const accounts =
51
+ info.accounts && typeof info.accounts === "object" ? info.accounts : {};
52
+ if (Object.keys(accounts).length > 0) {
53
+ return Object.values(accounts).some(
54
+ (acc) => acc && acc.status !== "paired",
55
+ );
56
+ }
57
+ return info.status !== "paired";
46
58
  });
47
59
 
48
60
  const pairingsPoll = usePolling(
@@ -54,14 +66,17 @@ export const useGeneralTab = ({
54
66
  { enabled: hasUnpaired && gatewayStatus === "running" },
55
67
  );
56
68
  const pending = pairingsPoll.data || [];
69
+ const shouldPollDevices =
70
+ gatewayStatus === "running" && (devicePollingEnabled || !cliAutoApproveComplete);
57
71
 
58
72
  const devicePoll = usePolling(
59
73
  async () => {
60
74
  const data = await fetchDevicePairings();
75
+ setCliAutoApproveComplete(data?.cliAutoApproveComplete === true);
61
76
  return data.pending || [];
62
77
  },
63
78
  2000,
64
- { enabled: gatewayStatus === "running" },
79
+ { enabled: shouldPollDevices },
65
80
  );
66
81
  const devicePending = devicePoll.data || [];
67
82
 
@@ -69,23 +84,38 @@ export const useGeneralTab = ({
69
84
  if (!isActive) return;
70
85
  onRefreshStatuses();
71
86
  pairingsPoll.refresh();
72
- devicePoll.refresh();
73
- }, [devicePoll.refresh, isActive, onRefreshStatuses, pairingsPoll.refresh]);
87
+ if (shouldPollDevices) {
88
+ devicePoll.refresh();
89
+ }
90
+ }, [
91
+ devicePoll.refresh,
92
+ isActive,
93
+ onRefreshStatuses,
94
+ pairingsPoll.refresh,
95
+ devicePollingEnabled,
96
+ shouldPollDevices,
97
+ ]);
74
98
 
75
99
  useEffect(() => {
76
100
  if (!restartSignal || !isActive) return;
77
101
  onRefreshStatuses();
78
102
  pairingsPoll.refresh();
79
- devicePoll.refresh();
103
+ if (shouldPollDevices) {
104
+ devicePoll.refresh();
105
+ }
80
106
  const t1 = setTimeout(() => {
81
107
  onRefreshStatuses();
82
108
  pairingsPoll.refresh();
83
- devicePoll.refresh();
109
+ if (shouldPollDevices) {
110
+ devicePoll.refresh();
111
+ }
84
112
  }, 1200);
85
113
  const t2 = setTimeout(() => {
86
114
  onRefreshStatuses();
87
115
  pairingsPoll.refresh();
88
- devicePoll.refresh();
116
+ if (shouldPollDevices) {
117
+ devicePoll.refresh();
118
+ }
89
119
  }, 3500);
90
120
  return () => {
91
121
  clearTimeout(t1);
@@ -97,6 +127,8 @@ export const useGeneralTab = ({
97
127
  onRefreshStatuses,
98
128
  pairingsPoll.refresh,
99
129
  restartSignal,
130
+ devicePollingEnabled,
131
+ shouldPollDevices,
100
132
  ]);
101
133
 
102
134
  useEffect(() => {
@@ -108,10 +140,34 @@ export const useGeneralTab = ({
108
140
  );
109
141
  }, [syncCron?.enabled, syncCron?.schedule]);
110
142
 
143
+ useEffect(
144
+ () => () => {
145
+ if (pairingRefreshTimerRef.current) {
146
+ clearTimeout(pairingRefreshTimerRef.current);
147
+ }
148
+ },
149
+ [],
150
+ );
151
+
111
152
  const refreshAfterPairingAction = () => {
112
- setTimeout(pairingsPoll.refresh, 500);
113
- setTimeout(pairingsPoll.refresh, 2000);
114
- setTimeout(onRefreshStatuses, 3000);
153
+ setPairingStatusRefreshing(true);
154
+ if (pairingRefreshTimerRef.current) {
155
+ clearTimeout(pairingRefreshTimerRef.current);
156
+ }
157
+ pairingRefreshTimerRef.current = setTimeout(() => {
158
+ setPairingStatusRefreshing(false);
159
+ pairingRefreshTimerRef.current = null;
160
+ }, 2800);
161
+ onRefreshStatuses();
162
+ pairingsPoll.refresh();
163
+ setTimeout(() => {
164
+ onRefreshStatuses();
165
+ pairingsPoll.refresh();
166
+ }, 700);
167
+ setTimeout(() => {
168
+ onRefreshStatuses();
169
+ pairingsPoll.refresh();
170
+ }, 1800);
115
171
  };
116
172
 
117
173
  const saveSyncCronSettings = async ({
@@ -146,14 +202,18 @@ export const useGeneralTab = ({
146
202
  });
147
203
  };
148
204
 
149
- const handleApprove = async (id, channel) => {
150
- await approvePairing(id, channel);
205
+ const handleApprove = async (id, channel, accountId = "") => {
206
+ await approvePairing(id, channel, accountId);
151
207
  refreshAfterPairingAction();
152
208
  };
153
209
 
154
- const handleReject = async (id, channel) => {
155
- await rejectPairing(id, channel);
156
- refreshAfterPairingAction();
210
+ const handleReject = async (id, channel, accountId = "") => {
211
+ try {
212
+ await rejectPairing(id, channel, accountId);
213
+ refreshAfterPairingAction();
214
+ } catch (err) {
215
+ showToast(err.message || "Could not reject pairing", "error");
216
+ }
157
217
  };
158
218
 
159
219
  const handleDeviceApprove = async (id) => {
@@ -187,6 +247,7 @@ export const useGeneralTab = ({
187
247
 
188
248
  const handleOpenDashboard = async () => {
189
249
  if (dashboardLoading) return;
250
+ setDevicePollingEnabled(true);
190
251
  setDashboardLoading(true);
191
252
  try {
192
253
  const data = await fetchDashboardUrl();
@@ -210,6 +271,7 @@ export const useGeneralTab = ({
210
271
  hasUnpaired,
211
272
  openclawVersion,
212
273
  pending,
274
+ pairingStatusRefreshing,
213
275
  repairingWatchdog,
214
276
  repo,
215
277
  savingSyncCron,
@@ -135,9 +135,7 @@ export const GmailSetupWizard = ({
135
135
  String(projectIdInput || "").trim() ||
136
136
  String(clientConfig?.projectId || "").trim() ||
137
137
  "<project-id>";
138
- const hasExistingWebhookSetup = Boolean(
139
- clientConfig?.configured && clientConfig?.transformExists,
140
- );
138
+ const hasExistingWebhookSetup = Boolean(clientConfig?.webhookExists);
141
139
  const client =
142
140
  String(account?.client || clientConfig?.client || "default").trim() ||
143
141
  "default";
@@ -17,6 +17,7 @@ import { CredentialsModal } from "../credentials-modal.js";
17
17
  import { ConfirmDialog } from "../confirm-dialog.js";
18
18
  import { showToast } from "../toast.js";
19
19
  import { ActionButton } from "../action-button.js";
20
+ import { OverflowMenu, OverflowMenuItem } from "../overflow-menu.js";
20
21
  import { GoogleAccountRow } from "./account-row.js";
21
22
  import { AddGoogleAccountModal } from "./add-account-modal.js";
22
23
  import { useGoogleAccounts } from "./use-google-accounts.js";
@@ -455,37 +456,34 @@ export const Google = ({
455
456
  ${accounts.length
456
457
  ? html`
457
458
  <div class="relative">
458
- <button
459
- type="button"
460
- onclick=${() => setAddMenuOpen((prev) => !prev)}
461
- class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-secondary"
459
+ <${OverflowMenu}
460
+ open=${addMenuOpen}
461
+ ariaLabel="Add Google account"
462
+ title="Add Google account"
463
+ onClose=${() => setAddMenuOpen(false)}
464
+ onToggle=${() => setAddMenuOpen((prev) => !prev)}
465
+ renderTrigger=${({ onToggle, ariaLabel, title }) => html`
466
+ <${ActionButton}
467
+ onClick=${onToggle}
468
+ tone="subtle"
469
+ size="sm"
470
+ idleLabel="+ Add Account"
471
+ ariaLabel=${ariaLabel}
472
+ title=${title}
473
+ />
474
+ `}
462
475
  >
463
- + Add Account
464
- </button>
465
- ${addMenuOpen
466
- ? html`
467
- <div
468
- class="absolute right-0 top-full mt-2 min-w-[210px] rounded-lg border border-border bg-modal p-1 z-20"
469
- >
470
- <button
471
- type="button"
472
- onclick=${handleAddCompanyClick}
473
- class="w-full text-left px-2.5 py-1.5 text-xs rounded-md hover:bg-black/30"
474
- >
475
- Company account
476
- </button>
477
- ${!hasPersonalAccount
478
- ? html`<button
479
- type="button"
480
- onclick=${handleAddPersonalClick}
481
- class="w-full text-left px-2.5 py-1.5 text-xs rounded-md hover:bg-black/30"
482
- >
483
- Personal account
484
- </button>`
485
- : null}
486
- </div>
487
- `
488
- : null}
476
+ <${OverflowMenuItem} onClick=${handleAddCompanyClick}>
477
+ Company account
478
+ </${OverflowMenuItem}>
479
+ ${!hasPersonalAccount
480
+ ? html`
481
+ <${OverflowMenuItem} onClick=${handleAddPersonalClick}>
482
+ Personal account
483
+ </${OverflowMenuItem}>
484
+ `
485
+ : null}
486
+ </${OverflowMenu}>
489
487
  </div>
490
488
  `
491
489
  : null}
@@ -41,6 +41,30 @@ export const CloseIcon = ({ className = "" }) => html`
41
41
  </svg>
42
42
  `;
43
43
 
44
+ export const AddLineIcon = ({ className = "" }) => html`
45
+ <svg
46
+ class=${className}
47
+ viewBox="0 0 24 24"
48
+ fill="currentColor"
49
+ aria-hidden="true"
50
+ >
51
+ <path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z" />
52
+ </svg>
53
+ `;
54
+
55
+ export const More2FillIcon = ({ className = "" }) => html`
56
+ <svg
57
+ class=${className}
58
+ viewBox="0 0 24 24"
59
+ fill="currentColor"
60
+ aria-hidden="true"
61
+ >
62
+ <path
63
+ d="M5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10ZM12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10ZM19 10C17.8954 10 17 10.8954 17 12C17 13.1046 17.8954 14 19 14C20.1046 14 21 13.1046 21 12C21 10.8954 20.1046 10 19 10Z"
64
+ />
65
+ </svg>
66
+ `;
67
+
44
68
  export const HomeLineIcon = ({ className = "" }) => html`
45
69
  <svg
46
70
  class=${className}
@@ -67,6 +91,19 @@ export const FolderLineIcon = ({ className = "" }) => html`
67
91
  </svg>
68
92
  `;
69
93
 
94
+ export const RobotLineIcon = ({ className = "" }) => html`
95
+ <svg
96
+ class=${className}
97
+ viewBox="0 0 24 24"
98
+ fill="currentColor"
99
+ aria-hidden="true"
100
+ >
101
+ <path
102
+ d="M13.5 2C13.5 2.44425 13.3069 2.84339 13 3.11805V5H18C19.6569 5 21 6.34315 21 8V18C21 19.6569 19.6569 21 18 21H6C4.34315 21 3 19.6569 3 18V8C3 6.34315 4.34315 5 6 5H11V3.11805C10.6931 2.84339 10.5 2.44425 10.5 2C10.5 1.17157 11.1716 0.5 12 0.5C12.8284 0.5 13.5 1.17157 13.5 2ZM6 7C5.44772 7 5 7.44772 5 8V18C5 18.5523 5.44772 19 6 19H18C18.5523 19 19 18.5523 19 18V8C19 7.44772 18.5523 7 18 7H13H11H6ZM2 10H0V16H2V10ZM22 10H24V16H22V10ZM9 14.5C9.82843 14.5 10.5 13.8284 10.5 13C10.5 12.1716 9.82843 11.5 9 11.5C8.17157 11.5 7.5 12.1716 7.5 13C7.5 13.8284 8.17157 14.5 9 14.5ZM15 14.5C15.8284 14.5 16.5 13.8284 16.5 13C16.5 12.1716 15.8284 11.5 15 11.5C14.1716 11.5 13.5 12.1716 13.5 13C13.5 13.8284 14.1716 14.5 15 14.5Z"
103
+ />
104
+ </svg>
105
+ `;
106
+
70
107
  export const MarkdownFillIcon = ({ className = "" }) => html`
71
108
  <svg
72
109
  class=${className}