@chrysb/alphaclaw 0.5.6 → 0.5.7-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
@@ -28,9 +28,15 @@ import {
28
28
  kTelegramWorkspaceCacheKey,
29
29
  } from "../../lib/storage-keys.js";
30
30
 
31
- const loadTelegramWorkspaceState = () => {
31
+ const resolveStorageKey = (baseKey, accountId) => {
32
+ const suffix = String(accountId || "").trim();
33
+ if (!suffix || suffix === "default") return baseKey;
34
+ return `${baseKey}.${suffix}`;
35
+ };
36
+
37
+ const loadTelegramWorkspaceState = (accountId) => {
32
38
  try {
33
- const raw = window.localStorage.getItem(kTelegramWorkspaceStorageKey);
39
+ const raw = window.localStorage.getItem(resolveStorageKey(kTelegramWorkspaceStorageKey, accountId));
34
40
  if (!raw) return {};
35
41
  const parsed = JSON.parse(raw);
36
42
  return parsed && typeof parsed === "object" ? parsed : {};
@@ -38,9 +44,22 @@ const loadTelegramWorkspaceState = () => {
38
44
  return {};
39
45
  }
40
46
  };
41
- const loadTelegramWorkspaceCache = () => {
47
+ const saveTelegramWorkspaceState = (accountId, state) => {
48
+ try {
49
+ window.localStorage.setItem(
50
+ resolveStorageKey(kTelegramWorkspaceStorageKey, accountId),
51
+ JSON.stringify(state),
52
+ );
53
+ } catch {}
54
+ };
55
+ const removeTelegramWorkspaceState = (accountId) => {
56
+ try {
57
+ window.localStorage.removeItem(resolveStorageKey(kTelegramWorkspaceStorageKey, accountId));
58
+ } catch {}
59
+ };
60
+ const loadTelegramWorkspaceCache = (accountId) => {
42
61
  try {
43
- const raw = window.localStorage.getItem(kTelegramWorkspaceCacheKey);
62
+ const raw = window.localStorage.getItem(resolveStorageKey(kTelegramWorkspaceCacheKey, accountId));
44
63
  if (!raw) return null;
45
64
  const parsed = JSON.parse(raw);
46
65
  const data = parsed?.data;
@@ -50,14 +69,19 @@ const loadTelegramWorkspaceCache = () => {
50
69
  return null;
51
70
  }
52
71
  };
53
- const saveTelegramWorkspaceCache = (data) => {
72
+ const saveTelegramWorkspaceCache = (accountId, data) => {
54
73
  try {
55
74
  window.localStorage.setItem(
56
- kTelegramWorkspaceCacheKey,
75
+ resolveStorageKey(kTelegramWorkspaceCacheKey, accountId),
57
76
  JSON.stringify({ cachedAt: Date.now(), data }),
58
77
  );
59
78
  } catch {}
60
79
  };
80
+ const removeTelegramWorkspaceCache = (accountId) => {
81
+ try {
82
+ window.localStorage.removeItem(resolveStorageKey(kTelegramWorkspaceCacheKey, accountId));
83
+ } catch {}
84
+ };
61
85
 
62
86
  const BackButton = ({ onBack }) => html`
63
87
  <button
@@ -73,15 +97,84 @@ const BackButton = ({ onBack }) => html`
73
97
  </button>
74
98
  `;
75
99
 
76
- export const TelegramWorkspace = ({ onBack }) => {
77
- const initialState = loadTelegramWorkspaceState();
78
- const cachedWorkspace = loadTelegramWorkspaceCache();
100
+ const MultiGroupView = ({
101
+ accountId,
102
+ groups,
103
+ concurrency,
104
+ debugEnabled,
105
+ onResetOnboarding,
106
+ }) => {
107
+ const [expandedGroupId, setExpandedGroupId] = useState(
108
+ () => groups[0]?.groupId || "",
109
+ );
110
+
111
+ const toggle = (gId) =>
112
+ setExpandedGroupId((current) => (current === gId ? "" : gId));
113
+
114
+ return html`
115
+ <div class="space-y-3">
116
+ ${groups.map(
117
+ (g) => html`
118
+ <div
119
+ key=${g.groupId}
120
+ class="border border-border rounded-lg overflow-hidden"
121
+ >
122
+ <button
123
+ onclick=${() => toggle(g.groupId)}
124
+ class="w-full flex items-center justify-between px-3 py-2.5 bg-black/20 hover:bg-black/30 transition-colors text-left"
125
+ >
126
+ <div>
127
+ <p class="text-sm text-gray-300 font-medium">
128
+ ${g.groupName || g.groupId}
129
+ </p>
130
+ <p class="text-[11px] text-gray-500 font-mono">${g.groupId}</p>
131
+ </div>
132
+ <svg
133
+ width="16"
134
+ height="16"
135
+ viewBox="0 0 16 16"
136
+ fill="currentColor"
137
+ class="text-gray-500 transition-transform ${expandedGroupId ===
138
+ g.groupId
139
+ ? "rotate-180"
140
+ : ""}"
141
+ >
142
+ <path
143
+ d="M4.354 5.646a.5.5 0 00-.708.708l4 4a.5.5 0 00.708 0l4-4a.5.5 0 00-.708-.708L8 9.293 4.354 5.646z"
144
+ />
145
+ </svg>
146
+ </button>
147
+ ${expandedGroupId === g.groupId &&
148
+ html`
149
+ <div class="p-3 border-t border-border">
150
+ <${ManageTelegramWorkspace}
151
+ accountId=${accountId}
152
+ groupId=${g.groupId}
153
+ groupName=${g.groupName}
154
+ initialTopics=${g.topics}
155
+ configAgentMaxConcurrent=${concurrency?.agentMaxConcurrent}
156
+ configSubagentMaxConcurrent=${concurrency?.subagentMaxConcurrent}
157
+ debugEnabled=${debugEnabled}
158
+ onResetOnboarding=${onResetOnboarding}
159
+ />
160
+ </div>
161
+ `}
162
+ </div>
163
+ `,
164
+ )}
165
+ </div>
166
+ `;
167
+ };
168
+
169
+ export const TelegramWorkspace = ({ accountId = "default", onBack }) => {
170
+ const initialState = loadTelegramWorkspaceState(accountId);
171
+ const cachedWorkspace = loadTelegramWorkspaceCache(accountId);
79
172
  const [step, setStep] = useState(() => {
80
173
  const value = Number.parseInt(String(initialState.step ?? 0), 10);
81
174
  if (!Number.isFinite(value)) return 0;
82
175
  return Math.min(Math.max(value, 0), kSteps.length - 1);
83
176
  });
84
- const [botInfo, setBotInfo] = useState(initialState.botInfo || null);
177
+ const [botInfo, setBotInfo] = useState(null);
85
178
  const [groupId, setGroupId] = useState(initialState.groupId || "");
86
179
  const [groupInfo, setGroupInfo] = useState(initialState.groupInfo || null);
87
180
  const [verifyGroupError, setVerifyGroupError] = useState(
@@ -94,6 +187,7 @@ export const TelegramWorkspace = ({ onBack }) => {
94
187
  const [workspaceConfig, setWorkspaceConfig] = useState(() => ({
95
188
  ready: !!cachedWorkspace,
96
189
  configured: !!cachedWorkspace?.configured,
190
+ groups: cachedWorkspace?.groups || [],
97
191
  groupId: cachedWorkspace?.groupId || "",
98
192
  groupName: cachedWorkspace?.groupName || "",
99
193
  topics: cachedWorkspace?.topics || {},
@@ -108,12 +202,10 @@ export const TelegramWorkspace = ({ onBack }) => {
108
202
  const goBack = () => setStep((s) => Math.max(0, s - 1));
109
203
  const resetOnboarding = async () => {
110
204
  try {
111
- const data = await api.resetWorkspace();
205
+ const data = await api.resetWorkspace({ accountId });
112
206
  if (!data.ok) throw new Error(data.error || "Failed to reset onboarding");
113
- try {
114
- window.localStorage.removeItem(kTelegramWorkspaceStorageKey);
115
- window.localStorage.removeItem(kTelegramWorkspaceCacheKey);
116
- } catch {}
207
+ removeTelegramWorkspaceState(accountId);
208
+ removeTelegramWorkspaceCache(accountId);
117
209
  setStep(0);
118
210
  setBotInfo(null);
119
211
  setGroupId("");
@@ -124,6 +216,7 @@ export const TelegramWorkspace = ({ onBack }) => {
124
216
  setWorkspaceConfig({
125
217
  ready: true,
126
218
  configured: false,
219
+ groups: [],
127
220
  groupId: "",
128
221
  groupName: "",
129
222
  topics: {},
@@ -136,48 +229,36 @@ export const TelegramWorkspace = ({ onBack }) => {
136
229
  }
137
230
  };
138
231
  const handleDone = () => {
139
- try {
140
- window.localStorage.removeItem(kTelegramWorkspaceStorageKey);
141
- window.localStorage.setItem(
142
- kTelegramWorkspaceCacheKey,
143
- JSON.stringify({
144
- cachedAt: Date.now(),
145
- data: {
146
- ready: true,
147
- configured: true,
148
- groupId,
149
- groupName: groupInfo?.chat?.title || groupId,
150
- topics: topics || {},
151
- debugEnabled: !!workspaceConfig?.debugEnabled,
152
- concurrency: workspaceConfig?.concurrency || {
153
- agentMaxConcurrent: null,
154
- subagentMaxConcurrent: null,
155
- },
156
- },
157
- }),
158
- );
159
- } catch {}
232
+ removeTelegramWorkspaceState(accountId);
233
+ const doneGroupName = groupInfo?.chat?.title || groupId;
234
+ saveTelegramWorkspaceCache(accountId, {
235
+ ready: true,
236
+ configured: true,
237
+ groups: [{ groupId, groupName: doneGroupName, topics: topics || {} }],
238
+ groupId,
239
+ groupName: doneGroupName,
240
+ topics: topics || {},
241
+ debugEnabled: !!workspaceConfig?.debugEnabled,
242
+ concurrency: workspaceConfig?.concurrency || {
243
+ agentMaxConcurrent: null,
244
+ subagentMaxConcurrent: null,
245
+ },
246
+ });
160
247
  window.location.reload();
161
248
  };
162
249
 
163
250
  useEffect(() => {
164
- try {
165
- window.localStorage.setItem(
166
- kTelegramWorkspaceStorageKey,
167
- JSON.stringify({
168
- step,
169
- botInfo,
170
- groupId,
171
- groupInfo,
172
- verifyGroupError,
173
- allowUserId,
174
- topics,
175
- }),
176
- );
177
- } catch {}
251
+ saveTelegramWorkspaceState(accountId, {
252
+ step,
253
+ groupId,
254
+ groupInfo,
255
+ verifyGroupError,
256
+ allowUserId,
257
+ topics,
258
+ });
178
259
  }, [
260
+ accountId,
179
261
  step,
180
- botInfo,
181
262
  groupId,
182
263
  groupInfo,
183
264
  verifyGroupError,
@@ -189,12 +270,14 @@ export const TelegramWorkspace = ({ onBack }) => {
189
270
  let active = true;
190
271
  const bootstrapWorkspace = async () => {
191
272
  try {
192
- const data = await api.workspace();
273
+ const data = await api.workspace({ accountId });
193
274
  if (!active || !data?.ok) return;
194
- if (!data.configured || !data.groupId) {
275
+ const groups = Array.isArray(data.groups) ? data.groups : [];
276
+ if (!data.configured || groups.length === 0) {
195
277
  const nextConfig = {
196
278
  ready: true,
197
279
  configured: false,
280
+ groups: [],
198
281
  groupId: "",
199
282
  groupName: "",
200
283
  topics: {},
@@ -205,15 +288,17 @@ export const TelegramWorkspace = ({ onBack }) => {
205
288
  },
206
289
  };
207
290
  setWorkspaceConfig(nextConfig);
208
- saveTelegramWorkspaceCache(nextConfig);
291
+ saveTelegramWorkspaceCache(accountId, nextConfig);
209
292
  return;
210
293
  }
294
+ const first = groups[0];
211
295
  const nextConfig = {
212
296
  ready: true,
213
297
  configured: true,
214
- groupId: data.groupId,
215
- groupName: data.groupName || data.groupId,
216
- topics: data.topics || {},
298
+ groups,
299
+ groupId: first.groupId,
300
+ groupName: first.groupName || first.groupId,
301
+ topics: first.topics || {},
217
302
  debugEnabled: !!data.debugEnabled,
218
303
  concurrency: data.concurrency || {
219
304
  agentMaxConcurrent: null,
@@ -221,13 +306,13 @@ export const TelegramWorkspace = ({ onBack }) => {
221
306
  },
222
307
  };
223
308
  setWorkspaceConfig(nextConfig);
224
- saveTelegramWorkspaceCache(nextConfig);
225
- setGroupId(data.groupId);
226
- setTopics(data.topics || {});
309
+ saveTelegramWorkspaceCache(accountId, nextConfig);
310
+ setGroupId(first.groupId);
311
+ setTopics(first.topics || {});
227
312
  setGroupInfo({
228
313
  chat: {
229
- id: data.groupId,
230
- title: data.groupName || data.groupId,
314
+ id: first.groupId,
315
+ title: first.groupName || first.groupId,
231
316
  isForum: true,
232
317
  },
233
318
  bot: {
@@ -245,7 +330,7 @@ export const TelegramWorkspace = ({ onBack }) => {
245
330
  return () => {
246
331
  active = false;
247
332
  };
248
- }, []);
333
+ }, [accountId]);
249
334
 
250
335
  return html`
251
336
  <div class="space-y-4">
@@ -271,17 +356,30 @@ export const TelegramWorkspace = ({ onBack }) => {
271
356
  </h2>
272
357
  </div>
273
358
  </div>
274
- <${ManageTelegramWorkspace}
275
- groupId=${workspaceConfig.groupId}
276
- groupName=${workspaceConfig.groupName}
277
- initialTopics=${workspaceConfig.topics}
278
- configAgentMaxConcurrent=${workspaceConfig.concurrency
279
- ?.agentMaxConcurrent}
280
- configSubagentMaxConcurrent=${workspaceConfig.concurrency
281
- ?.subagentMaxConcurrent}
282
- debugEnabled=${workspaceConfig.debugEnabled}
283
- onResetOnboarding=${resetOnboarding}
284
- />
359
+ ${(workspaceConfig.groups || []).length <= 1
360
+ ? html`
361
+ <${ManageTelegramWorkspace}
362
+ accountId=${accountId}
363
+ groupId=${workspaceConfig.groupId}
364
+ groupName=${workspaceConfig.groupName}
365
+ initialTopics=${workspaceConfig.topics}
366
+ configAgentMaxConcurrent=${workspaceConfig.concurrency
367
+ ?.agentMaxConcurrent}
368
+ configSubagentMaxConcurrent=${workspaceConfig.concurrency
369
+ ?.subagentMaxConcurrent}
370
+ debugEnabled=${workspaceConfig.debugEnabled}
371
+ onResetOnboarding=${resetOnboarding}
372
+ />
373
+ `
374
+ : html`
375
+ <${MultiGroupView}
376
+ accountId=${accountId}
377
+ groups=${workspaceConfig.groups}
378
+ concurrency=${workspaceConfig.concurrency}
379
+ debugEnabled=${workspaceConfig.debugEnabled}
380
+ onResetOnboarding=${resetOnboarding}
381
+ />
382
+ `}
285
383
  `
286
384
  : html`
287
385
  <div class="flex items-center justify-between mb-4">
@@ -305,6 +403,7 @@ export const TelegramWorkspace = ({ onBack }) => {
305
403
  ${step === 0 &&
306
404
  html`
307
405
  <${VerifyBotStep}
406
+ accountId=${accountId}
308
407
  botInfo=${botInfo}
309
408
  setBotInfo=${setBotInfo}
310
409
  onNext=${goNext}
@@ -317,6 +416,7 @@ export const TelegramWorkspace = ({ onBack }) => {
317
416
  ${step === 2 &&
318
417
  html`
319
418
  <${AddBotStep}
419
+ accountId=${accountId}
320
420
  groupId=${groupId}
321
421
  setGroupId=${setGroupId}
322
422
  groupInfo=${groupInfo}
@@ -332,6 +432,7 @@ export const TelegramWorkspace = ({ onBack }) => {
332
432
  ${step === 3 &&
333
433
  html`
334
434
  <${TopicsStep}
435
+ accountId=${accountId}
335
436
  groupId=${groupId}
336
437
  topics=${topics}
337
438
  setTopics=${setTopics}
@@ -5,10 +5,25 @@ import { showToast } from "../toast.js";
5
5
  import { ActionButton } from "../action-button.js";
6
6
  import { ConfirmDialog } from "../confirm-dialog.js";
7
7
  import * as api from "../../lib/telegram-api.js";
8
+ import { fetchAgents } from "../../lib/api.js";
8
9
 
9
10
  const html = htm.bind(h);
10
11
 
12
+ const AgentSelect = ({ value, agents, onChange, className = "" }) => html`
13
+ <select
14
+ value=${value}
15
+ onChange=${(e) => onChange(e.target.value)}
16
+ class="bg-black/30 border border-border rounded-lg px-2 py-1.5 text-xs text-gray-200 focus:outline-none focus:border-gray-500 ${className}"
17
+ >
18
+ <option value="">Default</option>
19
+ ${agents.map(
20
+ (a) => html`<option value=${a.id}>${a.name || a.id}</option>`,
21
+ )}
22
+ </select>
23
+ `;
24
+
11
25
  export const ManageTelegramWorkspace = ({
26
+ accountId,
12
27
  groupId,
13
28
  groupName,
14
29
  initialTopics,
@@ -20,18 +35,21 @@ export const ManageTelegramWorkspace = ({
20
35
  const [topics, setTopics] = useState(initialTopics || {});
21
36
  const [newTopicName, setNewTopicName] = useState("");
22
37
  const [newTopicInstructions, setNewTopicInstructions] = useState("");
38
+ const [newTopicAgentId, setNewTopicAgentId] = useState("");
23
39
  const [showCreateTopic, setShowCreateTopic] = useState(false);
24
40
  const [creating, setCreating] = useState(false);
25
41
  const [deleting, setDeleting] = useState(null);
26
42
  const [editingTopicId, setEditingTopicId] = useState("");
27
43
  const [editingTopicName, setEditingTopicName] = useState("");
28
44
  const [editingTopicInstructions, setEditingTopicInstructions] = useState("");
45
+ const [editingTopicAgentId, setEditingTopicAgentId] = useState("");
29
46
  const [renamingTopicId, setRenamingTopicId] = useState("");
30
47
  const [error, setError] = useState(null);
31
48
  const [deleteTopicConfirm, setDeleteTopicConfirm] = useState(null);
49
+ const [agents, setAgents] = useState([]);
32
50
 
33
51
  const loadTopics = async () => {
34
- const data = await api.listTopics(groupId);
52
+ const data = await api.listTopics(groupId, { accountId });
35
53
  if (data.ok) setTopics(data.topics || {});
36
54
  };
37
55
 
@@ -44,22 +62,34 @@ export const ManageTelegramWorkspace = ({
44
62
  }
45
63
  }, [initialTopics]);
46
64
 
65
+ useEffect(() => {
66
+ fetchAgents()
67
+ .then((data) => setAgents(Array.isArray(data?.agents) ? data.agents : []))
68
+ .catch(() => {});
69
+ }, []);
70
+
47
71
  const createSingle = async () => {
48
72
  const name = newTopicName.trim();
49
73
  const systemInstructions = newTopicInstructions.trim();
74
+ const agentId = newTopicAgentId.trim();
50
75
  if (!name) return;
51
76
  setCreating(true);
52
77
  setError(null);
53
78
  try {
54
79
  const data = await api.createTopicsBulk(groupId, [
55
- { name, ...(systemInstructions ? { systemInstructions } : {}) },
56
- ]);
80
+ {
81
+ name,
82
+ ...(systemInstructions ? { systemInstructions } : {}),
83
+ ...(agentId ? { agentId } : {}),
84
+ },
85
+ ], { accountId });
57
86
  if (!data.ok)
58
87
  throw new Error(data.results?.[0]?.error || "Failed to create topic");
59
88
  const failed = data.results.filter((r) => !r.ok);
60
89
  if (failed.length > 0) throw new Error(failed[0].error);
61
90
  setNewTopicName("");
62
91
  setNewTopicInstructions("");
92
+ setNewTopicAgentId("");
63
93
  setShowCreateTopic(false);
64
94
  await loadTopics();
65
95
  showToast(`Created topic: ${name}`, "success");
@@ -72,7 +102,7 @@ export const ManageTelegramWorkspace = ({
72
102
  const handleDelete = async (topicId, topicName) => {
73
103
  setDeleting(topicId);
74
104
  try {
75
- const data = await api.deleteTopic(groupId, topicId);
105
+ const data = await api.deleteTopic(groupId, topicId, { accountId });
76
106
  if (!data.ok) throw new Error(data.error);
77
107
  await loadTopics();
78
108
  if (data.removedFromRegistryOnly) {
@@ -86,21 +116,24 @@ export const ManageTelegramWorkspace = ({
86
116
  setDeleting(null);
87
117
  };
88
118
 
89
- const startRename = (topicId, topicName, topicInstructions = "") => {
119
+ const startRename = (topicId, topicName, topicInstructions = "", topicAgentId = "") => {
90
120
  setEditingTopicId(String(topicId));
91
121
  setEditingTopicName(String(topicName || ""));
92
122
  setEditingTopicInstructions(String(topicInstructions || ""));
123
+ setEditingTopicAgentId(String(topicAgentId || ""));
93
124
  };
94
125
 
95
126
  const cancelRename = () => {
96
127
  setEditingTopicId("");
97
128
  setEditingTopicName("");
98
129
  setEditingTopicInstructions("");
130
+ setEditingTopicAgentId("");
99
131
  };
100
132
 
101
133
  const saveRename = async (topicId) => {
102
134
  const nextName = editingTopicName.trim();
103
135
  const nextSystemInstructions = editingTopicInstructions.trim();
136
+ const nextAgentId = editingTopicAgentId.trim();
104
137
  if (!nextName) {
105
138
  setError("Topic name is required");
106
139
  return;
@@ -111,10 +144,11 @@ export const ManageTelegramWorkspace = ({
111
144
  const data = await api.updateTopic(groupId, topicId, {
112
145
  name: nextName,
113
146
  systemInstructions: nextSystemInstructions,
114
- });
115
- if (!data.ok) throw new Error(data.error || "Failed to rename topic");
147
+ agentId: nextAgentId,
148
+ }, { accountId });
149
+ if (!data.ok) throw new Error(data.error || "Failed to update topic");
116
150
  await loadTopics();
117
- showToast(`Renamed topic: ${nextName}`, "success");
151
+ showToast(`Updated topic: ${nextName}`, "success");
118
152
  cancelRename();
119
153
  } catch (e) {
120
154
  setError(e.message);
@@ -169,6 +203,14 @@ export const ManageTelegramWorkspace = ({
169
203
  >
170
204
  Thread ID
171
205
  </th>
206
+ ${agents.length > 0 &&
207
+ html`
208
+ <th
209
+ class="text-left px-3 py-2 text-gray-500 font-medium w-32"
210
+ >
211
+ Agent
212
+ </th>
213
+ `}
172
214
  <th class="px-3 py-2 w-28" />
173
215
  </tr>
174
216
  </thead>
@@ -180,7 +222,7 @@ export const ManageTelegramWorkspace = ({
180
222
  <tr
181
223
  class="border-b border-border last:border-0 align-top"
182
224
  >
183
- <td class="px-3 py-2" colspan="3">
225
+ <td class="px-3 py-2" colspan=${agents.length > 0 ? 4 : 3}>
184
226
  <div class="space-y-2">
185
227
  <input
186
228
  type="text"
@@ -203,6 +245,17 @@ export const ManageTelegramWorkspace = ({
203
245
  rows="6"
204
246
  class="w-full bg-black/30 border border-border rounded-lg px-2 py-1.5 text-xs text-gray-200 placeholder-gray-600 focus:outline-none focus:border-gray-500 resize-y"
205
247
  />
248
+ ${agents.length > 0 &&
249
+ html`
250
+ <div class="flex items-center gap-2">
251
+ <label class="text-xs text-gray-500">Agent:</label>
252
+ <${AgentSelect}
253
+ value=${editingTopicAgentId}
254
+ agents=${agents}
255
+ onChange=${setEditingTopicAgentId}
256
+ />
257
+ </div>
258
+ `}
206
259
  <div class="flex items-center gap-2">
207
260
  <button
208
261
  onclick=${() => saveRename(id)}
@@ -239,10 +292,11 @@ export const ManageTelegramWorkspace = ({
239
292
  id,
240
293
  topic.name,
241
294
  topic.systemInstructions,
295
+ topic.agentId,
242
296
  )}
243
297
  class="inline-flex items-center justify-center text-white/80 hover:text-white transition-colors"
244
298
  title="Edit topic"
245
- aria-label="Rename topic"
299
+ aria-label="Edit topic"
246
300
  >
247
301
  <svg
248
302
  width="14"
@@ -271,6 +325,14 @@ export const ManageTelegramWorkspace = ({
271
325
  >
272
326
  ${id}
273
327
  </td>
328
+ ${agents.length > 0 &&
329
+ html`
330
+ <td class="px-3 py-2 text-gray-400 w-32">
331
+ ${topic.agentId
332
+ ? html`<span class="text-gray-300">${agents.find((a) => a.id === topic.agentId)?.name || topic.agentId}</span>`
333
+ : html`<span class="text-gray-600">default</span>`}
334
+ </td>
335
+ `}
274
336
  <td class="px-3 py-2">
275
337
  <div
276
338
  class="flex items-center gap-2 justify-end"
@@ -325,6 +387,17 @@ export const ManageTelegramWorkspace = ({
325
387
  rows="5"
326
388
  class="w-full bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 placeholder-gray-600 focus:outline-none focus:border-gray-500 resize-y"
327
389
  />
390
+ ${agents.length > 0 &&
391
+ html`
392
+ <div class="flex items-center gap-2">
393
+ <label class="text-xs text-gray-500">Agent:</label>
394
+ <${AgentSelect}
395
+ value=${newTopicAgentId}
396
+ agents=${agents}
397
+ onChange=${setNewTopicAgentId}
398
+ />
399
+ </div>
400
+ `}
328
401
  <div class="flex justify-end">
329
402
  <${ActionButton}
330
403
  onClick=${createSingle}
@@ -25,7 +25,7 @@ export const StepIndicator = ({ currentStep, steps }) => html`
25
25
  `;
26
26
 
27
27
  // Step 1: Verify Bot
28
- export const VerifyBotStep = ({ botInfo, setBotInfo, onNext }) => {
28
+ export const VerifyBotStep = ({ accountId, botInfo, setBotInfo, onNext }) => {
29
29
  const [loading, setLoading] = useState(false);
30
30
  const [error, setError] = useState(null);
31
31
 
@@ -33,7 +33,7 @@ export const VerifyBotStep = ({ botInfo, setBotInfo, onNext }) => {
33
33
  setLoading(true);
34
34
  setError(null);
35
35
  try {
36
- const data = await api.verifyBot();
36
+ const data = await api.verifyBot({ accountId });
37
37
  if (!data.ok) throw new Error(data.error);
38
38
  setBotInfo(data.bot);
39
39
  } catch (e) {
@@ -180,6 +180,7 @@ export const CreateGroupStep = ({ onNext, onBack }) => html`
180
180
 
181
181
  // Step 3: Add Bot to Group / Verify Group
182
182
  export const AddBotStep = ({
183
+ accountId,
183
184
  groupId,
184
185
  setGroupId,
185
186
  groupInfo,
@@ -216,7 +217,7 @@ export const AddBotStep = ({
216
217
  setLoading(true);
217
218
  setVerifyGroupError(null);
218
219
  try {
219
- const data = await api.verifyGroup(id);
220
+ const data = await api.verifyGroup(id, { accountId });
220
221
  if (!data.ok) throw new Error(data.error);
221
222
  setGroupId(id);
222
223
  setGroupInfo(data);
@@ -245,7 +246,7 @@ export const AddBotStep = ({
245
246
  ...(userIdValue ? { userId: userIdValue } : {}),
246
247
  groupName: groupInfo?.chat?.title || groupId,
247
248
  requireMention: false,
248
- });
249
+ }, { accountId });
249
250
  if (!data?.ok)
250
251
  throw new Error(data?.error || "Failed to configure Telegram group");
251
252
  if (data.userId) setUserId(String(data.userId));
@@ -370,7 +371,7 @@ export const AddBotStep = ({
370
371
  };
371
372
 
372
373
  // Step 4: Create Topics
373
- export const TopicsStep = ({ groupId, topics, setTopics, onNext, onBack }) => {
374
+ export const TopicsStep = ({ accountId, groupId, topics, setTopics, onNext, onBack }) => {
374
375
  const [newTopicName, setNewTopicName] = useState("");
375
376
  const [newTopicInstructions, setNewTopicInstructions] = useState("");
376
377
  const [creating, setCreating] = useState(false);
@@ -379,7 +380,7 @@ export const TopicsStep = ({ groupId, topics, setTopics, onNext, onBack }) => {
379
380
  const [deleteTopicConfirm, setDeleteTopicConfirm] = useState(null);
380
381
 
381
382
  const loadTopics = async () => {
382
- const data = await api.listTopics(groupId);
383
+ const data = await api.listTopics(groupId, { accountId });
383
384
  if (data.ok) setTopics(data.topics);
384
385
  };
385
386
 
@@ -396,7 +397,7 @@ export const TopicsStep = ({ groupId, topics, setTopics, onNext, onBack }) => {
396
397
  try {
397
398
  const data = await api.createTopicsBulk(groupId, [
398
399
  { name, ...(systemInstructions ? { systemInstructions } : {}) },
399
- ]);
400
+ ], { accountId });
400
401
  if (!data.ok)
401
402
  throw new Error(data.results?.[0]?.error || "Failed to create topic");
402
403
  const failed = data.results.filter((r) => !r.ok);
@@ -414,7 +415,7 @@ export const TopicsStep = ({ groupId, topics, setTopics, onNext, onBack }) => {
414
415
  const handleDelete = async (topicId, topicName) => {
415
416
  setDeleting(topicId);
416
417
  try {
417
- const data = await api.deleteTopic(groupId, topicId);
418
+ const data = await api.deleteTopic(groupId, topicId, { accountId });
418
419
  if (!data.ok) throw new Error(data.error);
419
420
  await loadTopics();
420
421
  if (data.removedFromRegistryOnly) {