@chrysb/alphaclaw 0.6.2-beta.5 → 0.7.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 (33) hide show
  1. package/lib/public/css/agents.css +37 -13
  2. package/lib/public/css/cron.css +124 -41
  3. package/lib/public/css/shell.css +61 -2
  4. package/lib/public/css/theme.css +2 -1
  5. package/lib/public/js/app.js +41 -33
  6. package/lib/public/js/components/agents-tab/agent-detail-panel.js +61 -49
  7. package/lib/public/js/components/agents-tab/agent-overview/index.js +9 -0
  8. package/lib/public/js/components/agents-tab/agent-overview/tools-card.js +54 -0
  9. package/lib/public/js/components/cron-tab/cron-calendar.js +297 -203
  10. package/lib/public/js/components/cron-tab/cron-helpers.js +48 -0
  11. package/lib/public/js/components/cron-tab/cron-insights-panel.js +294 -0
  12. package/lib/public/js/components/cron-tab/cron-job-detail.js +38 -363
  13. package/lib/public/js/components/cron-tab/cron-job-settings-card.js +233 -0
  14. package/lib/public/js/components/cron-tab/cron-overview.js +40 -19
  15. package/lib/public/js/components/cron-tab/cron-prompt-editor.js +173 -0
  16. package/lib/public/js/components/cron-tab/cron-run-history-panel.js +74 -62
  17. package/lib/public/js/components/cron-tab/cron-runs-trend-card.js +24 -24
  18. package/lib/public/js/components/cron-tab/index.js +170 -78
  19. package/lib/public/js/components/envars.js +187 -46
  20. package/lib/public/js/components/file-viewer/editor-surface.js +5 -1
  21. package/lib/public/js/components/file-viewer/use-editor-line-number-sync.js +36 -0
  22. package/lib/public/js/components/file-viewer/use-file-viewer.js +7 -23
  23. package/lib/public/js/components/file-viewer/utils.js +1 -5
  24. package/lib/public/js/components/models-tab/index.js +137 -133
  25. package/lib/public/js/components/models-tab/provider-auth-card.js +8 -1
  26. package/lib/public/js/components/models-tab/use-models.js +35 -8
  27. package/lib/public/js/components/onboarding/welcome-pairing-step.js +88 -59
  28. package/lib/public/js/components/pane-shell.js +27 -0
  29. package/lib/public/js/components/routes/envars-route.js +1 -3
  30. package/lib/public/js/components/routes/models-route.js +1 -3
  31. package/lib/public/js/lib/app-navigation.js +1 -1
  32. package/lib/server/cost-utils.js +2 -2
  33. package/package.json +1 -1
@@ -1,34 +1,63 @@
1
1
  /* ── Agents detail layout ────────────────────── */
2
2
 
3
3
  .app-content-pane.agents-pane {
4
- padding: 0;
4
+ overflow: hidden;
5
+ padding-left: 0;
6
+ padding-right: 0;
7
+ padding-bottom: 0;
5
8
  }
6
9
 
7
10
  /* ── Detail panel ────────────────────────────── */
8
11
 
9
12
  .agents-detail-panel {
10
13
  height: 100%;
11
- overflow-y: auto;
12
- padding: 0 32px;
14
+ display: flex;
15
+ flex-direction: column;
16
+ min-height: 0;
17
+ }
18
+
19
+ .agents-detail-header-area {
20
+ padding: 8px 32px 16px;
13
21
  }
14
22
 
15
- .agents-detail-inner {
23
+ .agents-detail-header-area-inner {
16
24
  max-width: 42rem;
17
25
  width: 100%;
18
26
  margin: 0 auto;
19
- display: flex;
20
- flex-direction: column;
21
- min-height: 100%;
22
27
  }
23
28
 
24
29
  .agents-detail-header {
25
- padding-top: 16px;
26
30
  display: flex;
27
31
  align-items: flex-start;
28
32
  justify-content: space-between;
29
33
  gap: 12px;
30
34
  }
31
35
 
36
+ .agents-detail-body {
37
+ flex: 1;
38
+ min-height: 0;
39
+ overflow-y: auto;
40
+ overflow-x: hidden;
41
+ padding: 0 32px;
42
+ }
43
+
44
+ .agents-detail-content {
45
+ max-width: 42rem;
46
+ width: 100%;
47
+ margin: 0 auto;
48
+ padding: 0 0 24px;
49
+ }
50
+
51
+ @media (max-width: 768px) {
52
+ .agents-detail-header-area {
53
+ padding: 16px 14px 0;
54
+ }
55
+
56
+ .agents-detail-body {
57
+ padding: 0 14px;
58
+ }
59
+ }
60
+
32
61
  .agents-detail-header-title {
33
62
  font-size: 16px;
34
63
  font-weight: 600;
@@ -72,11 +101,6 @@
72
101
  border-bottom-color: var(--accent);
73
102
  }
74
103
 
75
- .agents-detail-content {
76
- flex: 1;
77
- padding: 16px 0;
78
- }
79
-
80
104
  /* ── Empty state ─────────────────────────────── */
81
105
 
82
106
  .agents-empty-state {
@@ -1,5 +1,6 @@
1
1
  .app-content-pane.cron-pane {
2
- padding: 0;
2
+ padding: 24px 32px 12px;
3
+ overflow: hidden;
3
4
  }
4
5
 
5
6
  .cron-tab-shell {
@@ -10,26 +11,115 @@
10
11
  }
11
12
 
12
13
  .cron-tab-header {
13
- padding: 16px 20px 12px;
14
- border-bottom: 1px solid var(--border);
14
+ padding: 0 0 16px;
15
+ }
16
+
17
+ .cron-tab-header-content {
18
+ width: 100%;
19
+ max-width: 672px;
20
+ margin-left: auto;
21
+ margin-right: auto;
22
+ box-sizing: border-box;
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 10px;
15
26
  }
16
27
 
17
28
  .cron-tab-main {
18
29
  flex: 1;
19
30
  min-height: 0;
31
+ overflow-y: auto;
32
+ overflow-x: hidden;
33
+ }
34
+
35
+ .cron-tab-main-content {
36
+ width: 100%;
37
+ max-width: 672px;
38
+ margin-left: auto;
39
+ margin-right: auto;
40
+ padding: 0 0 24px;
41
+ min-height: 100%;
42
+ }
43
+
44
+ .cron-tab-main-content .cron-detail-content {
45
+ padding-top: 8px;
46
+ }
47
+
48
+ @media (max-width: 768px) {
49
+ .app-content-pane.cron-pane {
50
+ padding: 0 14px 12px;
51
+ }
52
+
53
+ .cron-tab-header {
54
+ padding: 0 0 12px;
55
+ }
56
+ }
57
+
58
+ .cron-tab-selector-shell {
59
+ position: relative;
60
+ width: min(100%, 320px);
61
+ }
62
+
63
+ .cron-tab-selector-toggle {
64
+ width: 100%;
65
+ border: 1px solid var(--border);
66
+ border-radius: 10px;
67
+ background: rgba(255, 255, 255, 0.015);
68
+ color: var(--text);
69
+ text-align: left;
70
+ font: inherit;
71
+ padding: 8px 10px;
20
72
  display: flex;
73
+ align-items: center;
74
+ justify-content: space-between;
75
+ gap: 8px;
21
76
  }
22
77
 
23
- .cron-list-panel {
24
- min-width: 220px;
25
- max-width: 480px;
26
- border-right: 1px solid var(--border);
78
+ .cron-tab-selector-toggle:hover {
79
+ border-color: rgba(148, 163, 184, 0.45);
80
+ }
81
+
82
+ .cron-tab-selector-toggle.is-open {
83
+ border-color: rgba(99, 235, 255, 0.55);
84
+ background: rgba(99, 235, 255, 0.08);
85
+ }
86
+
87
+ .cron-tab-selector-title {
88
+ font-size: 14px;
89
+ font-weight: 700;
90
+ color: var(--text);
91
+ white-space: nowrap;
92
+ overflow: hidden;
93
+ text-overflow: ellipsis;
94
+ }
95
+
96
+ .cron-tab-selector-caret {
97
+ color: var(--text-dim);
98
+ transition: transform 0.12s ease;
99
+ }
100
+
101
+ .cron-tab-selector-toggle.is-open .cron-tab-selector-caret {
102
+ transform: rotate(180deg);
103
+ }
104
+
105
+ .cron-tab-selector-dropdown {
106
+ position: absolute;
107
+ top: calc(100% + 8px);
108
+ left: 0;
109
+ width: min(100vw - 40px, 420px);
110
+ max-height: min(70vh, 620px);
111
+ border: 1px solid var(--border);
112
+ border-radius: 12px;
27
113
  background: var(--bg-sidebar);
28
- overflow-y: auto;
114
+ box-shadow: 0 14px 32px rgba(0, 0, 0, 0.45);
115
+ overflow: hidden;
116
+ z-index: 8;
29
117
  }
30
118
 
31
- .cron-list-panel-inner {
119
+ .cron-tab-selector-dropdown .cron-list-panel-inner {
32
120
  padding: 0 10px 10px;
121
+ max-height: min(70vh, 620px);
122
+ overflow-y: auto;
33
123
  }
34
124
 
35
125
  .cron-list-sticky-search {
@@ -168,49 +258,26 @@
168
258
  flex: 0 0 auto;
169
259
  }
170
260
 
171
- .cron-list-resizer {
172
- cursor: col-resize;
173
- position: relative;
174
- width: 6px;
175
- margin-left: -3px;
176
- z-index: 4;
177
- }
178
-
179
- .cron-list-resizer::after {
180
- content: "";
181
- position: absolute;
182
- left: 2px;
183
- top: 0;
184
- bottom: 0;
185
- width: 2px;
186
- background: transparent;
187
- transition: background 0.12s;
188
- }
189
-
190
- .cron-list-resizer:hover::after,
191
- .cron-list-resizer.is-resizing::after {
192
- background: rgba(99, 235, 255, 0.55);
193
- }
194
-
195
261
  .cron-detail-panel {
196
- flex: 1;
262
+ height: auto;
197
263
  min-width: 0;
198
- min-height: 0;
199
- overflow: hidden;
264
+ min-height: 100%;
265
+ overflow: visible;
200
266
  }
201
267
 
202
268
  .cron-detail-scroll {
203
- height: 100%;
204
- overflow-y: auto;
269
+ height: auto;
270
+ overflow: visible;
205
271
  }
206
272
 
207
273
  .cron-detail-content {
208
- padding: 16px 20px 24px;
274
+ padding: 16px 0 0;
209
275
  display: flex;
210
276
  flex-direction: column;
211
277
  gap: 12px;
212
278
  min-height: 100%;
213
- width: min(100%, 1024px);
279
+ width: 100%;
280
+ max-width: 672px;
214
281
  margin-left: auto;
215
282
  margin-right: auto;
216
283
  }
@@ -227,7 +294,7 @@
227
294
 
228
295
  .cron-prompt-editor-shell .file-viewer-editor-line-num-col {
229
296
  width: 44px;
230
- padding: 16px 8px 112px 0;
297
+ padding: 16px 10px 112px 0;
231
298
  }
232
299
 
233
300
  .cron-calendar-repeating-strip {
@@ -318,6 +385,22 @@
318
385
  border-bottom: 1px solid var(--border);
319
386
  border-right: 1px solid var(--border);
320
387
  background: rgba(0, 0, 0, 0.15);
388
+ position: sticky;
389
+ left: 0;
390
+ z-index: 2;
391
+ }
392
+
393
+ .cron-calendar-grid-header .cron-calendar-hour-cell {
394
+ top: 0;
395
+ z-index: 3;
396
+ }
397
+
398
+ .cron-calendar-grid-wrap {
399
+ position: relative;
400
+ }
401
+
402
+ .cron-calendar-grid-row .cron-calendar-hour-cell {
403
+ box-shadow: 1px 0 0 var(--border);
321
404
  }
322
405
 
323
406
  .cron-calendar-grid-cell {
@@ -63,6 +63,62 @@
63
63
  padding: 0;
64
64
  }
65
65
 
66
+ /* ── Fixed-header pane shell ────────────────────── */
67
+
68
+ .app-content-pane.ac-fixed-header-pane {
69
+ overflow: hidden;
70
+ padding-left: 0;
71
+ padding-right: 0;
72
+ padding-bottom: 0;
73
+ }
74
+
75
+ .ac-pane-shell {
76
+ height: 100%;
77
+ display: flex;
78
+ flex-direction: column;
79
+ min-height: 0;
80
+ }
81
+
82
+ .ac-pane-header {
83
+ padding: 16px 32px 16px;
84
+ }
85
+
86
+ .ac-pane-header-content {
87
+ width: 100%;
88
+ max-width: 672px;
89
+ margin-left: auto;
90
+ margin-right: auto;
91
+ }
92
+
93
+ .ac-pane-body {
94
+ flex: 1;
95
+ min-height: 0;
96
+ overflow-y: auto;
97
+ overflow-x: hidden;
98
+ padding: 0 32px;
99
+ }
100
+
101
+ .ac-pane-body-content {
102
+ width: 100%;
103
+ max-width: 672px;
104
+ margin-left: auto;
105
+ margin-right: auto;
106
+ padding-bottom: 24px;
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: 16px;
110
+ }
111
+
112
+ @media (max-width: 768px) {
113
+ .ac-pane-header {
114
+ padding: 16px 14px 12px;
115
+ }
116
+
117
+ .ac-pane-body {
118
+ padding: 0 14px;
119
+ }
120
+ }
121
+
66
122
  /* ── Sidebar ───────────────────────────────────── */
67
123
 
68
124
  .app-sidebar {
@@ -336,6 +392,7 @@
336
392
  }
337
393
  .app-content-pane {
338
394
  padding: 0 14px 12px;
395
+ top: 52px;
339
396
  }
340
397
 
341
398
  .sidebar-resizer {
@@ -346,7 +403,9 @@
346
403
  display: flex;
347
404
  align-items: center;
348
405
  justify-content: center;
349
- position: sticky;
406
+ position: absolute;
407
+ left: 0;
408
+ right: 0;
350
409
  top: 0;
351
410
  z-index: 15;
352
411
  background: var(--panel-bg-contrast);
@@ -355,7 +414,7 @@
355
414
  border-radius: 0;
356
415
  min-height: 52px;
357
416
  padding: 8px 14px;
358
- margin: 0 -14px 10px;
417
+ margin: 0;
359
418
  }
360
419
 
361
420
  .mobile-topbar.is-scrolled {
@@ -641,7 +641,8 @@ textarea:focus {
641
641
  }
642
642
 
643
643
  .ac-pop-actions-hidden {
644
- display: none;
644
+ visibility: hidden;
645
+ pointer-events: none;
645
646
  }
646
647
 
647
648
  .ac-pop-actions-in > * {
@@ -77,6 +77,8 @@ const App = () => {
77
77
 
78
78
  const isAgentsRoute = location.startsWith("/agents");
79
79
  const isCronRoute = location.startsWith("/cron");
80
+ const isEnvarsRoute = location.startsWith("/envars");
81
+ const isModelsRoute = location.startsWith("/models");
80
82
  const selectedAgentId = (() => {
81
83
  const match = location.match(/^\/agents\/([^/]+)/);
82
84
  return match ? decodeURIComponent(match[1]) : "";
@@ -211,6 +213,31 @@ const App = () => {
211
213
  />
212
214
 
213
215
  <div class="app-content">
216
+ <div
217
+ class=${`mobile-topbar ${shellState.mobileTopbarScrolled ? "is-scrolled" : ""}`}
218
+ >
219
+ <button
220
+ class="mobile-topbar-menu"
221
+ onclick=${() =>
222
+ shellActions.setMobileSidebarOpen((open) => !open)}
223
+ aria-label="Open menu"
224
+ aria-expanded=${shellState.mobileSidebarOpen ? "true" : "false"}
225
+ >
226
+ <svg
227
+ width="18"
228
+ height="18"
229
+ viewBox="0 0 16 16"
230
+ fill="currentColor"
231
+ >
232
+ <path
233
+ d="M2 3.75a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 3.75zm0 4.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 8zm0 4.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75z"
234
+ />
235
+ </svg>
236
+ </button>
237
+ <span class="mobile-topbar-title">
238
+ <span style="color: var(--accent)">alpha</span>claw
239
+ </span>
240
+ </div>
214
241
  <div
215
242
  class="app-content-pane browse-pane"
216
243
  style=${{ display: browseState.isBrowseRoute ? "block" : "none" }}
@@ -265,38 +292,25 @@ const App = () => {
265
292
  onSetLocation=${setLocation}
266
293
  />
267
294
  </div>
295
+ <div
296
+ class="app-content-pane ac-fixed-header-pane"
297
+ style=${{ display: isEnvarsRoute ? "block" : "none" }}
298
+ >
299
+ <${EnvarsRoute} onRestartRequired=${controllerActions.setRestartRequired} />
300
+ </div>
301
+ <div
302
+ class="app-content-pane ac-fixed-header-pane"
303
+ style=${{ display: isModelsRoute ? "block" : "none" }}
304
+ >
305
+ <${ModelsRoute} onRestartRequired=${controllerActions.setRestartRequired} />
306
+ </div>
268
307
  <div
269
308
  class="app-content-pane"
270
309
  onscroll=${shellActions.handlePaneScroll}
271
- style=${{ display: browseState.isBrowseRoute || isAgentsRoute || isCronRoute ? "none" : "block" }}
310
+ style=${{ display: browseState.isBrowseRoute || isAgentsRoute || isCronRoute || isEnvarsRoute || isModelsRoute ? "none" : "block" }}
272
311
  >
273
- <div
274
- class=${`mobile-topbar ${shellState.mobileTopbarScrolled ? "is-scrolled" : ""}`}
275
- >
276
- <button
277
- class="mobile-topbar-menu"
278
- onclick=${() =>
279
- shellActions.setMobileSidebarOpen((open) => !open)}
280
- aria-label="Open menu"
281
- aria-expanded=${shellState.mobileSidebarOpen ? "true" : "false"}
282
- >
283
- <svg
284
- width="18"
285
- height="18"
286
- viewBox="0 0 16 16"
287
- fill="currentColor"
288
- >
289
- <path
290
- d="M2 3.75a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 3.75zm0 4.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75A.75.75 0 012 8zm0 4.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H2.75a.75.75 0 01-.75-.75z"
291
- />
292
- </svg>
293
- </button>
294
- <span class="mobile-topbar-title">
295
- <span style="color: var(--accent)">alpha</span>claw
296
- </span>
297
- </div>
298
312
  <div class="max-w-2xl w-full mx-auto">
299
- ${!browseState.isBrowseRoute && !isAgentsRoute && !isCronRoute
313
+ ${!browseState.isBrowseRoute && !isAgentsRoute && !isCronRoute && !isEnvarsRoute && !isModelsRoute
300
314
  ? html`
301
315
  <${Switch}>
302
316
  <${Route} path="/general">
@@ -336,9 +350,6 @@ const App = () => {
336
350
  <${Route} path="/telegram">
337
351
  <${RouteRedirect} to="/telegram/default" />
338
352
  </${Route}>
339
- <${Route} path="/models">
340
- <${ModelsRoute} onRestartRequired=${controllerActions.setRestartRequired} />
341
- </${Route}>
342
353
  <${Route} path="/providers">
343
354
  <${RouteRedirect} to="/models" />
344
355
  </${Route}>
@@ -368,9 +379,6 @@ const App = () => {
368
379
  <${Route} path="/usage">
369
380
  <${UsageRoute} onSetLocation=${setLocation} />
370
381
  </${Route}>
371
- <${Route} path="/envars">
372
- <${EnvarsRoute} onRestartRequired=${controllerActions.setRestartRequired} />
373
- </${Route}>
374
382
  <${Route} path="/webhooks/:hookName">
375
383
  ${(params) => html`
376
384
  <${WebhooksRoute}
@@ -1,5 +1,5 @@
1
1
  import { h } from "https://esm.sh/preact";
2
- import { useState, useCallback } from "https://esm.sh/preact/hooks";
2
+ import { useState, useCallback, useMemo } from "https://esm.sh/preact/hooks";
3
3
  import htm from "https://esm.sh/htm";
4
4
  import { ActionButton } from "../action-button.js";
5
5
  import { Badge } from "../badge.js";
@@ -64,6 +64,12 @@ export const AgentDetailPanel = ({
64
64
 
65
65
  const isSaving = saving || savingTools;
66
66
 
67
+ const toolsSummary = useMemo(() => ({
68
+ profile: tools.profile,
69
+ enabledCount: (tools.toolStates || []).filter((t) => t.enabled).length,
70
+ totalCount: (tools.toolStates || []).length,
71
+ }), [tools.profile, tools.toolStates]);
72
+
67
73
  if (!agent) {
68
74
  return html`
69
75
  <div class="agents-detail-panel">
@@ -76,57 +82,61 @@ export const AgentDetailPanel = ({
76
82
 
77
83
  return html`
78
84
  <div class="agents-detail-panel">
79
- <div class="agents-detail-inner">
80
- <div class="agents-detail-header">
81
- <div class="min-w-0">
82
- <div class="flex items-center gap-2 min-w-0">
83
- <span class="agents-detail-header-title">
84
- ${agent.name || agent.id}
85
- </span>
86
- <button
87
- type="button"
88
- class="text-gray-500 hover:text-gray-300 transition-colors p-0.5 -ml-0.5"
89
- onclick=${() => onEdit(agent)}
90
- title="Edit agent name"
91
- >
92
- <${PencilIcon} />
93
- </button>
94
- ${agent.default
95
- ? html`<${Badge} tone="cyan">Default</${Badge}>`
96
- : null}
97
- </div>
98
- <div class="mt-1 flex flex-wrap items-center gap-x-1.5 gap-y-1 min-w-0 text-xs text-gray-500">
99
- <span class="font-mono">${agent.id}</span>
85
+ <div class="agents-detail-header-area">
86
+ <div class="agents-detail-header-area-inner">
87
+ <div class="agents-detail-header">
88
+ <div class="min-w-0">
89
+ <div class="flex items-center gap-2 min-w-0">
90
+ <span class="agents-detail-header-title">
91
+ ${agent.name || agent.id}
92
+ </span>
93
+ <button
94
+ type="button"
95
+ class="text-gray-500 hover:text-gray-300 transition-colors p-0.5 -ml-0.5"
96
+ onclick=${() => onEdit(agent)}
97
+ title="Edit agent name"
98
+ >
99
+ <${PencilIcon} />
100
+ </button>
101
+ ${agent.default
102
+ ? html`<${Badge} tone="cyan">Default</${Badge}>`
103
+ : null}
104
+ </div>
105
+ <div class="mt-1 flex flex-wrap items-center gap-x-1.5 gap-y-1 min-w-0 text-xs text-gray-500">
106
+ <span class="font-mono">${agent.id}</span>
107
+ </div>
100
108
  </div>
109
+ <${PopActions} visible=${tools.dirty}>
110
+ <${ActionButton}
111
+ onClick=${tools.reset}
112
+ disabled=${isSaving}
113
+ tone="secondary"
114
+ size="sm"
115
+ idleLabel="Cancel"
116
+ className="text-xs"
117
+ />
118
+ <${ActionButton}
119
+ onClick=${handleSaveTools}
120
+ disabled=${isSaving}
121
+ loading=${isSaving}
122
+ loadingMode="inline"
123
+ tone="primary"
124
+ size="sm"
125
+ idleLabel="Save changes"
126
+ loadingLabel="Saving…"
127
+ className="text-xs"
128
+ />
129
+ </${PopActions}>
101
130
  </div>
102
- <${PopActions} visible=${tools.dirty}>
103
- <${ActionButton}
104
- onClick=${tools.reset}
105
- disabled=${isSaving}
106
- tone="secondary"
107
- size="sm"
108
- idleLabel="Cancel"
109
- className="text-xs"
110
- />
111
- <${ActionButton}
112
- onClick=${handleSaveTools}
113
- disabled=${isSaving}
114
- loading=${isSaving}
115
- loadingMode="inline"
116
- tone="primary"
117
- size="sm"
118
- idleLabel="Save changes"
119
- loadingLabel="Saving…"
120
- className="text-xs"
121
- />
122
- </${PopActions}>
131
+ <${PillTabs}
132
+ tabs=${kDetailTabs}
133
+ activeTab=${activeTab}
134
+ onSelectTab=${onSelectTab}
135
+ className="flex items-center gap-2 pt-6"
136
+ />
123
137
  </div>
124
- <${PillTabs}
125
- tabs=${kDetailTabs}
126
- activeTab=${activeTab}
127
- onSelectTab=${onSelectTab}
128
- className="flex items-center gap-2 pt-6"
129
- />
138
+ </div>
139
+ <div class="agents-detail-body">
130
140
  <div class="agents-detail-content">
131
141
  ${activeTab === "overview"
132
142
  ? html`
@@ -134,10 +144,12 @@ export const AgentDetailPanel = ({
134
144
  agent=${agent}
135
145
  agents=${agents}
136
146
  saving=${saving}
147
+ toolsSummary=${toolsSummary}
137
148
  onUpdateAgent=${onUpdateAgent}
138
149
  onSetLocation=${onSetLocation}
139
150
  onOpenWorkspace=${onOpenWorkspace}
140
151
  onSwitchToModels=${() => onSetLocation("/models")}
152
+ onSwitchToTools=${() => onSelectTab("tools")}
141
153
  onSetDefault=${onSetDefault}
142
154
  onDelete=${onDelete}
143
155
  />
@@ -3,6 +3,7 @@ import htm from "https://esm.sh/htm";
3
3
  import { ChannelOperationsPanel } from "../../channel-operations-panel.js";
4
4
  import { ManageCard } from "./manage-card.js";
5
5
  import { AgentModelCard } from "./model-card.js";
6
+ import { AgentToolsCard } from "./tools-card.js";
6
7
  import { WorkspaceCard } from "./workspace-card.js";
7
8
 
8
9
  const html = htm.bind(h);
@@ -11,10 +12,12 @@ export const AgentOverview = ({
11
12
  agent = {},
12
13
  agents = [],
13
14
  saving = false,
15
+ toolsSummary = {},
14
16
  onUpdateAgent = async () => {},
15
17
  onSetLocation = () => {},
16
18
  onOpenWorkspace = () => {},
17
19
  onSwitchToModels = () => {},
20
+ onSwitchToTools = () => {},
18
21
  onSetDefault = () => {},
19
22
  onDelete = () => {},
20
23
  }) => {
@@ -33,6 +36,12 @@ export const AgentOverview = ({
33
36
  onUpdateAgent=${onUpdateAgent}
34
37
  onSwitchToModels=${onSwitchToModels}
35
38
  />
39
+ <${AgentToolsCard}
40
+ profile=${toolsSummary.profile || "full"}
41
+ enabledCount=${toolsSummary.enabledCount || 0}
42
+ totalCount=${toolsSummary.totalCount || 0}
43
+ onSwitchToTools=${onSwitchToTools}
44
+ />
36
45
  <${ChannelOperationsPanel}
37
46
  agent=${agent}
38
47
  agents=${agents}