@adminforth/agent 2.10.0 → 3.0.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.
@@ -8,19 +8,19 @@
8
8
  >
9
9
  <span class="flex items-center justify-center w-4 h-4 shrink-0 rounded-full" :class="statusClasses">
10
10
  <Spinner v-if="isRunning" class="w-3 h-3" />
11
- <IconXMark16Solid v-else-if="props.data.toolInfo.error" class="w-3 h-3" />
12
- <IconCheck16Solid v-else class="w-3 h-3" />
11
+ <IconX v-else-if="props.data.toolInfo.error" class="w-3 h-3" />
12
+ <IconCheck v-else class="w-3 h-3" />
13
13
  </span>
14
- <span class="max-w-[55%] shrink-0 truncate font-mono text-xs text-gray-800 dark:text-gray-200">
14
+ <span class="max-w-[55%] shrink-0 truncate font-mono text-xs text-lightInputText dark:text-darkInputText">
15
15
  {{ props.data.toolInfo.toolName }}
16
16
  </span>
17
- <span class="flex-1 min-w-0 truncate text-gray-500 dark:text-gray-400" :title="props.data.toolInfo.toolInfo">
17
+ <span class="flex-1 min-w-0 truncate text-lightInputIconColor dark:text-darkInputIconColor" :title="props.data.toolInfo.toolInfo">
18
18
  {{ props.data.toolInfo.toolInfo }}
19
19
  </span>
20
- <span v-if="durationLabel" class="shrink-0 tabular-nums text-gray-500 dark:text-gray-400">{{ durationLabel }}</span>
20
+ <span v-if="durationLabel" class="shrink-0 tabular-nums text-lightInputIconColor dark:text-darkInputIconColor">{{ durationLabel }}</span>
21
21
  <IconChevronDown
22
22
  v-if="hasToolSections"
23
- class="w-3.5 h-3.5 shrink-0 text-gray-400 transition-transform duration-200"
23
+ class="w-3.5 h-3.5 shrink-0 text-lightInputIconColor transition-transform duration-200 dark:text-darkInputIconColor"
24
24
  :class="isInputOutputExpanded ? 'rotate-180' : 'rotate-0'"
25
25
  />
26
26
  </button>
@@ -29,14 +29,14 @@
29
29
  <section
30
30
  v-for="section in toolSections"
31
31
  :key="section.label"
32
- class="overflow-hidden rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900/40"
32
+ class="overflow-hidden rounded-default border border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground"
33
33
  >
34
- <header class="border-b border-gray-200 px-2.5 py-1.5 text-[0.7rem] font-semibold uppercase tracking-wider text-gray-500 dark:border-gray-700 dark:text-gray-400">
34
+ <header class="border-b border-lightCardBorder px-2.5 py-1.5 text-xs font-medium text-lightInputIconColor dark:border-darkCardBorder dark:text-darkInputIconColor">
35
35
  {{ section.label }}
36
36
  </header>
37
- <div class="select-all grid grid-cols-[auto,1fr] gap-x-3 gap-y-0.5 px-2.5 py-2 font-mono text-xs leading-5 text-gray-700 dark:text-gray-200">
37
+ <div class="select-all grid grid-cols-[auto,1fr] gap-x-3 gap-y-0.5 px-2.5 py-2 font-mono text-xs leading-5 text-lightTableText dark:text-darkTableText">
38
38
  <template v-for="line in section.lines" :key="`${section.label}-${line.number}`">
39
- <span class="text-[0.7rem] text-gray-400 dark:text-gray-500 select-none">{{ line.number }}</span>
39
+ <span class="text-[0.7rem] text-lightInputPlaceholderText dark:text-darkInputPlaceholderText select-none">{{ line.number }}</span>
40
40
  <span class="whitespace-pre-wrap break-words">{{ line.content || ' ' }}</span>
41
41
  </template>
42
42
  </div>
@@ -50,7 +50,7 @@
50
50
  import { computed, ref } from 'vue';
51
51
  import { type IFormattedToolCallPart } from '../types';
52
52
  import { Spinner } from '@/afcl';
53
- import { IconCheck16Solid, IconChevronDown, IconXMark16Solid } from '@iconify-prerendered/vue-heroicons';
53
+ import { IconCheck, IconChevronDown, IconX } from '@iconify-prerendered/vue-lucide';
54
54
 
55
55
  const props = defineProps<{
56
56
  data: IFormattedToolCallPart
@@ -73,11 +73,11 @@
73
73
  });
74
74
  const statusClasses = computed(() => {
75
75
  if (isRunning.value) {
76
- return 'text-gray-500 dark:text-gray-400';
76
+ return 'text-lightInputIconColor dark:text-darkInputIconColor';
77
77
  }
78
78
  return props.data.toolInfo.error
79
- ? 'bg-red-100 text-red-600 dark:bg-red-500/20 dark:text-red-400'
80
- : 'bg-lightPrimary/10 text-lightPrimary dark:bg-darkPrimary/20 dark:text-darkPrimary';
79
+ ? 'bg-lightDangerSoft text-lightDanger dark:bg-darkDangerSoft dark:text-darkDanger'
80
+ : 'bg-lightSuccessSoft text-lightSuccess dark:bg-darkSuccessSoft dark:text-darkSuccess';
81
81
  });
82
82
  const toolSections = computed<IToolSection[]>(() => {
83
83
  const sections = [
@@ -205,6 +205,9 @@ async function renderHighlight() {
205
205
  const result = await embed(vegaContainer.value, spec, {
206
206
  actions: false,
207
207
  renderer: 'svg',
208
+ // the chart sits on the surface of the code block, so it takes the theme of the page
209
+ theme: codeTheme.value === 'dark' ? 'dark' : undefined,
210
+ config: { background: 'transparent' },
208
211
  });
209
212
 
210
213
  if (requestId !== renderRequestId) {
@@ -259,70 +262,36 @@ function clearVega() {
259
262
 
260
263
  <style scoped>
261
264
  .incremark-shiki-code {
262
- --incremark-code-border: rgba(148, 163, 184, 0.24);
263
- --incremark-code-toolbar-bg: rgba(248, 250, 252, 0.9);
264
- --incremark-code-toolbar-text: #475569;
265
- --incremark-code-copy-bg: rgba(226, 232, 240, 0.9);
266
- --incremark-code-copy-text: #0f172a;
267
- border: 1px solid var(--incremark-code-border);
268
- border-radius: 16px;
265
+ @apply border border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground;
266
+ border-radius: 8px;
269
267
  overflow: hidden;
270
268
  width: 100%;
271
- background:
272
- linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(248, 250, 252, 0.96));
273
- box-shadow: 0 14px 30px -24px rgba(15, 23, 42, 0.45);
274
- margin: 16px 0 ;
275
- }
276
-
277
- .dark .incremark-shiki-code {
278
- --incremark-code-border: rgba(100, 116, 139, 0.38);
279
- --incremark-code-toolbar-bg: rgba(15, 23, 42, 0.88);
280
- --incremark-code-toolbar-text: #cbd5e1;
281
- --incremark-code-copy-bg: rgba(30, 41, 59, 0.88);
282
- --incremark-code-copy-text: #e2e8f0;
283
- background:
284
- linear-gradient(180deg, rgba(2, 6, 23, 0.96), rgba(15, 23, 42, 0.98));
285
- }
286
-
287
- .incremark-shiki-code[data-stream-state='pending'],
288
- .incremark-shiki-code[data-stream-state='stable'] {
289
- box-shadow: 0 10px 24px -24px rgba(15, 23, 42, 0.8);
269
+ margin: 0 0 8px;
290
270
  }
291
271
 
292
272
  .incremark-shiki-toolbar {
273
+ @apply border-b border-lightCardBorder bg-lightCardBackgroundHover dark:border-darkCardBorder dark:bg-darkCardBackgroundHover;
293
274
  display: flex;
294
275
  align-items: center;
295
276
  justify-content: space-between;
296
277
  gap: 12px;
297
- padding: 10px 14px;
298
- border-bottom: 1px solid var(--incremark-code-border);
299
- background: var(--incremark-code-toolbar-bg);
300
- backdrop-filter: blur(10px);
278
+ padding: 4px 4px 4px 12px;
301
279
  }
302
280
 
303
281
  .incremark-shiki-language {
304
- font-size: 11px;
305
- font-weight: 600;
306
- letter-spacing: 0.12em;
307
- text-transform: uppercase;
308
- color: var(--incremark-code-toolbar-text);
282
+ @apply text-xs font-medium text-lightInputIconColor dark:text-darkInputIconColor;
309
283
  }
310
284
 
311
285
  .incremark-shiki-copy {
312
- border: none;
313
- border-radius: 999px;
314
- padding: 6px 10px;
315
- font-size: 11px;
316
- font-weight: 600;
317
- letter-spacing: 0.04em;
318
- background: var(--incremark-code-copy-bg);
319
- color: var(--incremark-code-copy-text);
320
- cursor: pointer;
321
- transition: transform 0.16s ease, opacity 0.16s ease, background-color 0.16s ease;
286
+ @apply h-6 rounded-default px-2 text-xs font-medium transition-colors text-lightInputIconColor dark:text-darkInputIconColor;
322
287
  }
323
288
 
324
289
  .incremark-shiki-copy:hover:not(:disabled) {
325
- transform: translateY(-1px);
290
+ @apply bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
291
+ }
292
+
293
+ .incremark-shiki-copy:focus-visible {
294
+ @apply outline-none ring-[3px] ring-lightFocusRing dark:ring-darkFocusRing;
326
295
  }
327
296
 
328
297
  .incremark-shiki-copy:disabled {
@@ -331,13 +300,7 @@ function clearVega() {
331
300
  }
332
301
 
333
302
  .incremark-shiki-copy.is-copied {
334
- background: rgba(16, 185, 129, 0.16);
335
- color: #059669;
336
- }
337
-
338
- .dark .incremark-shiki-copy.is-copied {
339
- background: rgba(16, 185, 129, 0.24);
340
- color: #6ee7b7;
303
+ @apply text-lightSuccess dark:text-darkSuccess;
341
304
  }
342
305
 
343
306
  .incremark-shiki-body {
@@ -345,13 +308,13 @@ function clearVega() {
345
308
  }
346
309
 
347
310
  .incremark-vega {
348
- padding: 18px;
311
+ padding: 12px;
349
312
  width: 100%;
350
313
  }
351
314
 
352
315
  .incremark-shiki-fallback {
353
316
  margin: 0;
354
- padding: 18px;
317
+ padding: 12px;
355
318
  overflow-x: auto;
356
319
  background: transparent;
357
320
  color: inherit;
@@ -359,7 +322,7 @@ function clearVega() {
359
322
 
360
323
  .incremark-shiki-fallback code {
361
324
  display: block;
362
- font-family: 'JetBrains Mono', 'Fira Code', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
325
+ font-family: theme('fontFamily.mono');
363
326
  font-size: 13px;
364
327
  line-height: 1.65;
365
328
  white-space: pre;
@@ -367,7 +330,7 @@ function clearVega() {
367
330
 
368
331
  :deep(.incremark-shiki-html pre.shiki) {
369
332
  margin: 0;
370
- padding: 18px;
333
+ padding: 12px;
371
334
  overflow-x: auto;
372
335
  background: transparent !important;
373
336
  font-size: 13px;
@@ -376,7 +339,7 @@ function clearVega() {
376
339
 
377
340
  :deep(.incremark-shiki-html code) {
378
341
  display: block;
379
- font-family: 'JetBrains Mono', 'Fira Code', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
342
+ font-family: theme('fontFamily.mono');
380
343
  }
381
344
 
382
345
  :deep(.incremark-shiki-html .line) {
@@ -387,7 +350,5 @@ function clearVega() {
387
350
  width: 100%;
388
351
  }
389
352
 
390
- :deep(.incremark-vega){
391
- padding: 0;
392
- }
353
+
393
354
  </style>
@@ -6,12 +6,14 @@
6
6
  <!-- One button for the whole voice flow: the icon shows the state, the label what a click does -->
7
7
  <button
8
8
  type="button"
9
- class="flex h-8 shrink-0 items-center justify-center rounded-full transition-colors duration-150"
9
+ class="flex h-[2.125rem] shrink-0 items-center justify-center rounded-full transition-colors duration-150
10
+ focus:outline-none focus-visible:ring-[3px] focus-visible:ring-lightFocusRing dark:focus-visible:ring-darkFocusRing"
10
11
  :class="isAudioChatMode
11
12
  ? `w-40 gap-2 px-3 text-sm font-medium
12
- bg-lightPrimary/10 text-gray-800 hover:bg-lightPrimary/15
13
- dark:bg-darkPrimary/20 dark:text-gray-100 dark:hover:bg-darkPrimary/30`
14
- : 'w-8 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'"
13
+ bg-lightPrimary/10 text-lightInputText hover:bg-lightPrimary/15
14
+ dark:bg-darkPrimary/20 dark:text-darkInputText dark:hover:bg-darkPrimary/30`
15
+ : `w-[2.125rem] text-lightInputIconColor hover:bg-lightSecondaryDarken hover:text-lightInputText
16
+ dark:text-darkInputIconColor dark:hover:bg-darkSecondaryLighten dark:hover:text-darkInputText`"
15
17
  :title="$t(buttonTitle)"
16
18
  :aria-label="$t(buttonTitle)"
17
19
  @click="isGenerating ? stopCurrentGeneration() : toggleChatMode()"
@@ -28,6 +28,10 @@ const sessionIdBodySchema = z.object({
28
28
  const createSessionBodySchema = z.object({
29
29
  triggerMessage: z.string().optional(),
30
30
  }).strict();
31
+ const renameSessionBodySchema = z.object({
32
+ sessionId: z.string(),
33
+ title: z.string().trim().min(1).max(100),
34
+ }).strict();
31
35
  export function setupSessionEndpoints(ctx, server) {
32
36
  server.endpoint({
33
37
  method: 'POST',
@@ -175,6 +179,36 @@ export function setupSessionEndpoints(ctx, server) {
175
179
  };
176
180
  })
177
181
  });
182
+ server.endpoint({
183
+ method: 'POST',
184
+ path: `/agent/rename-session`,
185
+ agent: {
186
+ hiddenFromAgents: true,
187
+ },
188
+ request_schema: renameSessionBodySchema,
189
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
190
+ const data = body;
191
+ const session = yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).get([Filters.EQ(ctx.options.sessionResource.idField, data.sessionId)]);
192
+ if (!session) {
193
+ response.setStatus(404, 'Session not found');
194
+ return {
195
+ error: 'Session not found'
196
+ };
197
+ }
198
+ if (session[ctx.options.sessionResource.askerIdField] !== adminUser.pk) {
199
+ response.setStatus(403, 'Unauthorized');
200
+ return {
201
+ error: 'Unauthorized'
202
+ };
203
+ }
204
+ yield ctx.adminforth.resource(ctx.options.sessionResource.resourceId).update(data.sessionId, {
205
+ [ctx.options.sessionResource.titleField]: data.title,
206
+ });
207
+ return {
208
+ title: data.title,
209
+ };
210
+ })
211
+ });
178
212
  server.endpoint({
179
213
  method: 'POST',
180
214
  path: `/agent/add-system-message-to-turns`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "2.10.0",
3
+ "version": "3.0.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@types/jest": "^30.0.0",
38
38
  "@types/node": "latest",
39
- "adminforth": "^3.32.0",
39
+ "adminforth": "^4.1.0",
40
40
  "jest": "^30.3.0",
41
41
  "semantic-release": "^24.2.1",
42
42
  "semantic-release-slack-bot": "^4.0.2",
@@ -80,6 +80,6 @@
80
80
  }
81
81
  ],
82
82
  "peerDependencies": {
83
- "adminforth": "^3.32.0"
83
+ "adminforth": "^4.1.0"
84
84
  }
85
85
  }
@@ -1,45 +0,0 @@
1
- <!--
2
- TEMPORARY debug helper: enqueues a test steer message into the buffer WITHOUT sending
3
- or steering it, so the SteerQueue enter/leave animation can be exercised without a live
4
- generating turn. Remove before shipping.
5
- -->
6
- <template>
7
- <div
8
- class="fixed bottom-[400px] left-4 z-[9999] flex items-center gap-2 rounded-lg border border-dashed border-amber-400 bg-amber-50/95 px-2.5 py-2 shadow-lg backdrop-blur dark:border-amber-500 dark:bg-amber-950/95"
9
- >
10
- <span class="text-[10px] font-bold uppercase tracking-wider text-amber-600 dark:text-amber-400">
11
- Steer&nbsp;debug
12
- </span>
13
- <input
14
- v-model="debugText"
15
- type="text"
16
- placeholder="Test steer text…"
17
- class="w-44 rounded-md border border-amber-300 bg-white px-2 py-1 text-xs text-gray-800 focus:outline-none focus:ring-1 focus:ring-amber-400 dark:border-amber-600 dark:bg-gray-800 dark:text-gray-100"
18
- @keydown.enter.prevent="addTestSteer"
19
- />
20
- <button
21
- type="button"
22
- class="rounded-md bg-amber-500 px-2.5 py-1 text-xs font-semibold text-white transition-colors hover:bg-amber-600 active:bg-amber-700"
23
- @click="addTestSteer"
24
- >
25
- Add steer
26
- </button>
27
- </div>
28
- </template>
29
-
30
- <script setup lang="ts">
31
- import { ref } from 'vue';
32
- import { useAgentStore } from './composables/useAgentStore';
33
-
34
- const agentStore = useAgentStore();
35
- const debugText = ref('');
36
- let counter = 0;
37
-
38
- function addTestSteer() {
39
- counter += 1;
40
- const text = debugText.value.trim() || `Test steer #${counter}`;
41
- // enqueue() only pushes onto the client-side buffer — no /agent/steer, no send.
42
- agentStore.enqueueSteerMessage(text);
43
- debugText.value = '';
44
- }
45
- </script>