@adminforth/agent 2.9.1 → 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.
- package/README.md +4 -0
- package/dist/application/runTurnUseCase.d.ts +2 -1
- package/dist/application/runTurnUseCase.js +3 -3
- package/dist/custom/ChatFooter.vue +91 -104
- package/dist/custom/ChatHeader.vue +63 -62
- package/dist/custom/ChatSurface.vue +11 -18
- package/dist/custom/EditActionButtons.vue +12 -8
- package/dist/custom/SessionsHistory.vue +90 -28
- package/dist/custom/SteerQueue.vue +15 -9
- package/dist/custom/composables/agentStore/constants.ts +1 -1
- package/dist/custom/composables/agentStore/useAgentChat.ts +4 -3
- package/dist/custom/composables/agentStore/useAgentPlaceholder.ts +1 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +23 -2
- package/dist/custom/composables/useAgentStore.ts +8 -29
- package/dist/custom/conversation_area/ConversationArea.vue +48 -16
- package/dist/custom/conversation_area/MessageRenderer.vue +18 -13
- package/dist/custom/conversation_area/ProcessingTimeline.vue +83 -123
- package/dist/custom/conversation_area/ReasoningRenderer.vue +13 -16
- package/dist/custom/conversation_area/SystemMessageRenderer.vue +4 -4
- package/dist/custom/conversation_area/TextRenderer.vue +67 -28
- package/dist/custom/conversation_area/ToolApprovalRenderer.vue +89 -56
- package/dist/custom/conversation_area/ToolRenderer.vue +44 -82
- package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +22 -61
- package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -2
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +36 -32
- package/dist/custom/types.ts +16 -8
- package/dist/domain/agentEvents.d.ts +3 -2
- package/dist/tools/approvalFormatter.d.ts +19 -5
- package/dist/tools/approvalFormatter.js +88 -40
- package/dist/transport/http/context.d.ts +2 -1
- package/dist/transport/http/sessionEndpoints.js +34 -0
- package/package.json +3 -3
- package/dist/custom/SteerDebugPanel.vue +0 -45
- package/dist/custom/conversation_area/ToolsGroup.vue +0 -59
|
@@ -1,79 +1,112 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="af-agent-approval mx-4 my-2 max-w-[min(34rem,calc(100%-2rem))] z-20 rounded-lg border p-3.5"
|
|
4
|
+
:class="isAwaitingDecision
|
|
5
|
+
? 'border-lightWarningBorder bg-lightWarningSoft dark:border-darkWarningBorder dark:bg-darkWarningSoft'
|
|
6
|
+
: 'border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground'"
|
|
7
|
+
>
|
|
8
|
+
<div class="flex items-start gap-2.5">
|
|
9
|
+
<IconShieldAlert
|
|
10
|
+
class="mt-0.5 h-4 w-4 shrink-0"
|
|
11
|
+
:class="isAwaitingDecision ? 'text-lightWarning dark:text-darkWarning' : 'text-lightInputIconColor dark:text-darkInputIconColor'"
|
|
12
|
+
/>
|
|
7
13
|
<div class="min-w-0 flex-1">
|
|
8
|
-
<h3 class="text-sm font-semibold leading-5">{{ $t('Approval
|
|
9
|
-
<p class="
|
|
14
|
+
<h3 class="text-sm font-semibold leading-5 text-lightCardTitle dark:text-darkCardTitle">{{ $t('Approval needed') }}</h3>
|
|
15
|
+
<p class="text-[0.8125rem] leading-5 text-lightCardDescription dark:text-darkCardDescription">
|
|
10
16
|
{{ $t('Review what the agent is about to do before continuing.') }}
|
|
11
17
|
</p>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<ul
|
|
21
|
+
class="mt-3 divide-y rounded-default border text-[0.8125rem] leading-5
|
|
22
|
+
divide-lightCardBorder border-lightCardBorder bg-lightCardBackground text-lightTableText
|
|
23
|
+
dark:divide-darkCardBorder dark:border-darkCardBorder dark:bg-darkCardBackground dark:text-darkTableText"
|
|
24
|
+
>
|
|
25
|
+
<li
|
|
26
|
+
v-for="(approval, approvalIndex) in data.approvals"
|
|
27
|
+
:key="approvalIndex"
|
|
28
|
+
class="px-3 py-2.5"
|
|
29
|
+
>
|
|
30
|
+
<p class="font-medium [overflow-wrap:anywhere]">{{ approval.summary }}</p>
|
|
31
|
+
<dl
|
|
32
|
+
v-if="approval.fields.length"
|
|
33
|
+
class="mt-2 grid grid-cols-[minmax(0,auto)_minmax(0,1fr)] gap-x-4 gap-y-1.5"
|
|
15
34
|
>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
type="button"
|
|
37
|
-
class="inline-flex h-9 items-center gap-2 rounded-md border border-lightListTableText/20 bg-transparent px-3 text-sm font-medium text-lightListTableHeadingText transition hover:bg-lightListTableText/10 dark:border-darkListTableText/20 dark:text-darkListTableHeadingText dark:hover:bg-darkListTableText/10"
|
|
38
|
-
@click="submit('reject')"
|
|
39
|
-
>
|
|
40
|
-
<IconCloseOutline class="h-4 w-4" />
|
|
41
|
-
{{ $t('Reject') }}
|
|
42
|
-
</button>
|
|
35
|
+
<template v-for="(field, fieldIndex) in approval.fields" :key="fieldIndex">
|
|
36
|
+
<dt class="text-lightInputIconColor dark:text-darkInputIconColor [overflow-wrap:anywhere]">{{ field.label }}</dt>
|
|
37
|
+
<dd class="flex min-w-0 flex-wrap items-center gap-1.5">
|
|
38
|
+
<template v-if="field.from !== undefined && field.from !== field.to">
|
|
39
|
+
<span
|
|
40
|
+
class="min-w-0 rounded px-1.5 line-through [overflow-wrap:anywhere]
|
|
41
|
+
bg-lightDangerSoft text-lightDanger decoration-lightDanger/50 dark:bg-darkDangerSoft dark:text-darkDanger dark:decoration-darkDanger/50"
|
|
42
|
+
>
|
|
43
|
+
{{ field.from }}
|
|
44
|
+
</span>
|
|
45
|
+
<IconArrowRight class="h-3.5 w-3.5 shrink-0 text-lightInputIconColor dark:text-darkInputIconColor" />
|
|
46
|
+
<span
|
|
47
|
+
class="min-w-0 rounded px-1.5 font-medium [overflow-wrap:anywhere]
|
|
48
|
+
bg-lightSuccessSoft text-lightSuccess dark:bg-darkSuccessSoft dark:text-darkSuccess"
|
|
49
|
+
>
|
|
50
|
+
{{ field.to }}
|
|
51
|
+
</span>
|
|
52
|
+
</template>
|
|
53
|
+
<span v-else class="min-w-0 font-medium [overflow-wrap:anywhere]">{{ field.to }}</span>
|
|
54
|
+
</dd>
|
|
43
55
|
</template>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
</dl>
|
|
57
|
+
</li>
|
|
58
|
+
</ul>
|
|
59
|
+
<div class="mt-3 flex flex-wrap gap-2">
|
|
60
|
+
<template v-if="data.status === 'pending'">
|
|
61
|
+
<Button size="sm" type="button" @click="submit('approve')">
|
|
62
|
+
<IconCheck class="h-4 w-4" />
|
|
63
|
+
{{ $t('Approve') }}
|
|
64
|
+
</Button>
|
|
65
|
+
<Button variant="secondary" size="sm" type="button" @click="submit('reject')">
|
|
66
|
+
{{ $t('Reject') }}
|
|
67
|
+
</Button>
|
|
68
|
+
</template>
|
|
69
|
+
<span
|
|
70
|
+
v-else
|
|
71
|
+
class="inline-flex h-6 items-center gap-1.5 rounded-full border px-2 text-xs font-medium"
|
|
72
|
+
:class="STATUS_BADGE_CLASSES[data.status]"
|
|
73
|
+
>
|
|
74
|
+
<Spinner v-if="data.status === 'processing'" class="h-3.5 w-3.5" />
|
|
75
|
+
<IconCheck v-else-if="data.status === 'approved'" class="h-3.5 w-3.5" />
|
|
76
|
+
<IconX v-else class="h-3.5 w-3.5" />
|
|
77
|
+
{{ data.status === 'processing' ? $t('Processing') : data.status === 'approved' ? $t('Approved') : $t('Rejected') }}
|
|
78
|
+
</span>
|
|
61
79
|
</div>
|
|
80
|
+
<p
|
|
81
|
+
v-if="data.error"
|
|
82
|
+
class="mt-3 text-sm leading-5 text-lightDanger dark:text-darkDanger"
|
|
83
|
+
>
|
|
84
|
+
{{ data.error }}
|
|
85
|
+
</p>
|
|
62
86
|
</div>
|
|
63
87
|
</template>
|
|
64
88
|
|
|
65
89
|
<script setup lang="ts">
|
|
90
|
+
import { computed } from 'vue';
|
|
66
91
|
import type { IPartData } from '../types';
|
|
67
92
|
import { useAgentStore } from '../composables/useAgentStore';
|
|
68
|
-
import { Spinner } from '@/afcl';
|
|
69
|
-
import {
|
|
93
|
+
import { Button, Spinner } from '@/afcl';
|
|
94
|
+
import { IconArrowRight, IconCheck, IconShieldAlert, IconX } from '@iconify-prerendered/vue-lucide';
|
|
70
95
|
|
|
71
96
|
const props = defineProps<{
|
|
72
97
|
data: IPartData;
|
|
73
98
|
}>();
|
|
74
99
|
|
|
100
|
+
const STATUS_BADGE_CLASSES: Record<string, string> = {
|
|
101
|
+
processing: 'border-transparent bg-lightSecondaryDarken text-lightInputIconColor dark:bg-darkSecondaryLighten dark:text-darkInputIconColor',
|
|
102
|
+
approved: 'border-lightSuccessBorder bg-lightSuccessSoft text-lightSuccess dark:border-darkSuccessBorder dark:bg-darkSuccessSoft dark:text-darkSuccess',
|
|
103
|
+
rejected: 'border-lightDangerBorder bg-lightDangerSoft text-lightDanger dark:border-darkDangerBorder dark:bg-darkDangerSoft dark:text-darkDanger',
|
|
104
|
+
};
|
|
105
|
+
|
|
75
106
|
const agentStore = useAgentStore();
|
|
76
107
|
|
|
108
|
+
const isAwaitingDecision = computed(() => props.data.status === 'pending' || props.data.status === 'processing');
|
|
109
|
+
|
|
77
110
|
function submit(decision: 'approve' | 'reject') {
|
|
78
111
|
if (!props.data.sessionId || props.data.status !== 'pending') {
|
|
79
112
|
return;
|
|
@@ -1,68 +1,56 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
flex-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
:class="[
|
|
9
|
-
isInputOutputExpanded ? 'items-start border-none' : '',
|
|
10
|
-
activateShrinkedStyle ? 'border items-center' : '',
|
|
11
|
-
activateFullWidth ? 'w-full' : '',
|
|
12
|
-
]"
|
|
13
|
-
:style="{
|
|
14
|
-
maxWidth: activateShrinkedStyle ? toolRendererInitialWidth + 'px' : '',
|
|
15
|
-
}"
|
|
16
|
-
>
|
|
17
|
-
<div
|
|
18
|
-
class="flex items-center gap-1 cursor-pointer"
|
|
19
|
-
@click="toggleInputOutput()"
|
|
2
|
+
<li>
|
|
3
|
+
<button
|
|
4
|
+
type="button"
|
|
5
|
+
class="flex items-center gap-2 w-full py-0.5 text-left text-[0.8125rem] leading-5 select-none"
|
|
6
|
+
:class="hasToolSections ? 'cursor-pointer' : 'cursor-default'"
|
|
7
|
+
@click="hasToolSections && (isInputOutputExpanded = !isInputOutputExpanded)"
|
|
20
8
|
>
|
|
21
|
-
<
|
|
22
|
-
<Spinner v-if="isRunning" class="
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
</
|
|
32
|
-
<
|
|
9
|
+
<span class="flex items-center justify-center w-4 h-4 shrink-0 rounded-full" :class="statusClasses">
|
|
10
|
+
<Spinner v-if="isRunning" 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
|
+
</span>
|
|
14
|
+
<span class="max-w-[55%] shrink-0 truncate font-mono text-xs text-lightInputText dark:text-darkInputText">
|
|
15
|
+
{{ props.data.toolInfo.toolName }}
|
|
16
|
+
</span>
|
|
17
|
+
<span class="flex-1 min-w-0 truncate text-lightInputIconColor dark:text-darkInputIconColor" :title="props.data.toolInfo.toolInfo">
|
|
18
|
+
{{ props.data.toolInfo.toolInfo }}
|
|
19
|
+
</span>
|
|
20
|
+
<span v-if="durationLabel" class="shrink-0 tabular-nums text-lightInputIconColor dark:text-darkInputIconColor">{{ durationLabel }}</span>
|
|
21
|
+
<IconChevronDown
|
|
33
22
|
v-if="hasToolSections"
|
|
23
|
+
class="w-3.5 h-3.5 shrink-0 text-lightInputIconColor transition-transform duration-200 dark:text-darkInputIconColor"
|
|
34
24
|
:class="isInputOutputExpanded ? 'rotate-180' : 'rotate-0'"
|
|
35
|
-
@transitionend="finishTransition()"
|
|
36
|
-
class="shrink-0 cursor-pointer transition-transform duration-300 hover:scale-105"
|
|
37
25
|
/>
|
|
38
|
-
</
|
|
26
|
+
</button>
|
|
39
27
|
<transition name="expand">
|
|
40
|
-
<div v-if="isInputOutputExpanded"
|
|
28
|
+
<div v-if="isInputOutputExpanded" class="max-h-72 mt-1.5 mb-1 ml-6 space-y-2 overflow-y-auto">
|
|
41
29
|
<section
|
|
42
30
|
v-for="section in toolSections"
|
|
43
31
|
:key="section.label"
|
|
44
|
-
class="overflow-hidden rounded-
|
|
32
|
+
class="overflow-hidden rounded-default border border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground"
|
|
45
33
|
>
|
|
46
|
-
<header class="border-b border-
|
|
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">
|
|
47
35
|
{{ section.label }}
|
|
48
36
|
</header>
|
|
49
|
-
<div class="select-all grid grid-cols-[auto,1fr] gap-x-3 gap-y-
|
|
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">
|
|
50
38
|
<template v-for="line in section.lines" :key="`${section.label}-${line.number}`">
|
|
51
|
-
<span class="
|
|
39
|
+
<span class="text-[0.7rem] text-lightInputPlaceholderText dark:text-darkInputPlaceholderText select-none">{{ line.number }}</span>
|
|
52
40
|
<span class="whitespace-pre-wrap break-words">{{ line.content || ' ' }}</span>
|
|
53
41
|
</template>
|
|
54
42
|
</div>
|
|
55
43
|
</section>
|
|
56
44
|
</div>
|
|
57
45
|
</transition>
|
|
58
|
-
</
|
|
46
|
+
</li>
|
|
59
47
|
</template>
|
|
60
48
|
|
|
61
49
|
<script setup lang="ts">
|
|
62
|
-
import { computed, ref
|
|
50
|
+
import { computed, ref } from 'vue';
|
|
63
51
|
import { type IFormattedToolCallPart } from '../types';
|
|
64
52
|
import { Spinner } from '@/afcl';
|
|
65
|
-
import {
|
|
53
|
+
import { IconCheck, IconChevronDown, IconX } from '@iconify-prerendered/vue-lucide';
|
|
66
54
|
|
|
67
55
|
const props = defineProps<{
|
|
68
56
|
data: IFormattedToolCallPart
|
|
@@ -77,14 +65,20 @@
|
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
const isInputOutputExpanded = ref(false);
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
|
|
69
|
+
const isRunning = computed(() => props.data.toolInfo.phase === 'start');
|
|
70
|
+
const durationLabel = computed(() => {
|
|
71
|
+
const { durationMs } = props.data.toolInfo;
|
|
72
|
+
return durationMs === undefined ? null : `${(durationMs / 1000).toFixed(1)}s`;
|
|
73
|
+
});
|
|
74
|
+
const statusClasses = computed(() => {
|
|
75
|
+
if (isRunning.value) {
|
|
76
|
+
return 'text-lightInputIconColor dark:text-darkInputIconColor';
|
|
77
|
+
}
|
|
78
|
+
return props.data.toolInfo.error
|
|
79
|
+
? 'bg-lightDangerSoft text-lightDanger dark:bg-darkDangerSoft dark:text-darkDanger'
|
|
80
|
+
: 'bg-lightSuccessSoft text-lightSuccess dark:bg-darkSuccessSoft dark:text-darkSuccess';
|
|
81
|
+
});
|
|
88
82
|
const toolSections = computed<IToolSection[]>(() => {
|
|
89
83
|
const sections = [
|
|
90
84
|
{
|
|
@@ -109,38 +103,6 @@
|
|
|
109
103
|
});
|
|
110
104
|
const hasToolSections = computed(() => toolSections.value.length > 0);
|
|
111
105
|
|
|
112
|
-
onMounted(async () => {
|
|
113
|
-
await nextTick();
|
|
114
|
-
if (toolRendererRef.value && props.data.toolInfo) {
|
|
115
|
-
const contentWidth = Math.ceil(toolRendererRef.value.getBoundingClientRect().width);
|
|
116
|
-
const parentWidth = toolRendererRef.value.parentElement?.clientWidth ?? Infinity;
|
|
117
|
-
toolRendererInitialWidth.value = Math.min(contentWidth, parentWidth);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
watch(isInputOutputExpanded, (newValue) => {
|
|
122
|
-
if (newValue) {
|
|
123
|
-
activateShrinkedStyle.value = false;
|
|
124
|
-
activateFullWidth.value = true;
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
function finishTransition() {
|
|
129
|
-
if (!isInputOutputExpanded.value) {
|
|
130
|
-
activateFullWidth.value = false;
|
|
131
|
-
activateShrinkedStyle.value = true;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function toggleInputOutput() {
|
|
136
|
-
if (blockClicksDuringAnimation.value) return;
|
|
137
|
-
isInputOutputExpanded.value = !isInputOutputExpanded.value;
|
|
138
|
-
blockClicksDuringAnimation.value = true;
|
|
139
|
-
setTimeout(() => {
|
|
140
|
-
blockClicksDuringAnimation.value = false;
|
|
141
|
-
}, ANIMATION_DURATION);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
106
|
const normalizeToolPayload = (value: unknown): string | null => {
|
|
145
107
|
if (value === null || value === undefined || value === '') {
|
|
146
108
|
return null;
|
|
@@ -175,4 +137,4 @@
|
|
|
175
137
|
max-height: 288px;
|
|
176
138
|
}
|
|
177
139
|
|
|
178
|
-
</style>
|
|
140
|
+
</style>
|
|
@@ -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
|
-
|
|
263
|
-
|
|
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
|
-
|
|
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:
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
311
|
+
padding: 12px;
|
|
349
312
|
width: 100%;
|
|
350
313
|
}
|
|
351
314
|
|
|
352
315
|
.incremark-shiki-fallback {
|
|
353
316
|
margin: 0;
|
|
354
|
-
padding:
|
|
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: '
|
|
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:
|
|
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: '
|
|
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
|
-
|
|
391
|
-
padding: 0;
|
|
392
|
-
}
|
|
353
|
+
|
|
393
354
|
</style>
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
<div
|
|
3
3
|
v-for="(height, index) in lineHeights"
|
|
4
4
|
:key="index"
|
|
5
|
-
class="bg-
|
|
5
|
+
class="bg-current w-[0.2rem] rounded-sm transition-all duration-100 ease-out"
|
|
6
6
|
:style="{ height }"
|
|
7
7
|
/>
|
|
8
|
-
<p v-if="isRecording" class="text-white ml-2">{{ $t('End') }}</p>
|
|
9
8
|
</template>
|
|
10
9
|
|
|
11
10
|
<script setup lang="ts">
|
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-show="!agentStore.isResponseInProgress || isAudioChatMode"
|
|
4
|
-
class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<button
|
|
8
|
-
v-if="isAudioChatMode && microphoneButtonMode === 'generating'"
|
|
9
|
-
class="bg-red-500 hover:bg-red-600 dark:bg-red-700 dark:hover:bg-red-800 h-9 flex items-center justify-center rounded-full gap-2 text-white px-4"
|
|
10
|
-
@click="stopCurrentGeneration()"
|
|
11
|
-
>
|
|
12
|
-
<div class="w-3 h-3 bg-white rounded-full"/>
|
|
13
|
-
{{ $t('Stop') }}
|
|
14
|
-
</button>
|
|
4
|
+
class="flex items-center"
|
|
5
|
+
>
|
|
6
|
+
<!-- One button for the whole voice flow: the icon shows the state, the label what a click does -->
|
|
15
7
|
<button
|
|
16
|
-
|
|
17
|
-
class="h-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
type="button"
|
|
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"
|
|
11
|
+
:class="isAudioChatMode
|
|
12
|
+
? `w-40 gap-2 px-3 text-sm font-medium
|
|
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`"
|
|
17
|
+
:title="$t(buttonTitle)"
|
|
18
|
+
:aria-label="$t(buttonTitle)"
|
|
19
|
+
@click="isGenerating ? stopCurrentGeneration() : toggleChatMode()"
|
|
22
20
|
>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</div>
|
|
21
|
+
<span
|
|
22
|
+
class="flex h-5 items-center gap-[0.1rem]"
|
|
23
|
+
:class="isAudioChatMode ? 'text-lightPrimary dark:text-darkPrimary' : ''"
|
|
24
|
+
>
|
|
25
|
+
<span v-if="isGenerating" class="w-2.5 h-2.5 rounded-sm bg-current" />
|
|
26
|
+
<Spinner v-else-if="isPolling || microphoneButtonMode === 'transcribing'" class="w-4 h-4" />
|
|
27
|
+
<AudioLines
|
|
28
|
+
v-else
|
|
29
|
+
:showAnimation="showAudioWavesAnimation"
|
|
30
|
+
:isRecording="microphoneButtonMode === 'listen'"
|
|
31
|
+
:amplitude="audioAmplitude"
|
|
32
|
+
/>
|
|
33
|
+
</span>
|
|
34
|
+
<template v-if="isAudioChatMode">{{ isGenerating ? $t('Stop') : $t('End') }}</template>
|
|
38
35
|
</button>
|
|
39
36
|
</div>
|
|
40
37
|
</template>
|
|
@@ -69,6 +66,13 @@ const sendUserRecordDebounced = debounce(() => {
|
|
|
69
66
|
const isPolling = ref(false);
|
|
70
67
|
|
|
71
68
|
const isAudioChatMode = computed(() => agentStore.isAudioChatMode);
|
|
69
|
+
const isGenerating = computed(() => microphoneButtonMode.value === 'generating');
|
|
70
|
+
const buttonTitle = computed(() => {
|
|
71
|
+
if (!isAudioChatMode.value) {
|
|
72
|
+
return 'Start voice chat';
|
|
73
|
+
}
|
|
74
|
+
return isGenerating.value ? 'Stop response' : 'End voice chat';
|
|
75
|
+
});
|
|
72
76
|
|
|
73
77
|
onMounted(() => {
|
|
74
78
|
agentStore.registerOnBeforeChatCloseCallback(async () => {
|
package/dist/custom/types.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/** One value on the approval card; `from` is the stored value an update replaces. */
|
|
2
|
+
export interface IApprovalField {
|
|
3
|
+
label: string;
|
|
4
|
+
from?: string;
|
|
5
|
+
to: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** A tool call awaiting approval, as described by the backend. */
|
|
9
|
+
export interface IApprovalDescription {
|
|
10
|
+
summary: string;
|
|
11
|
+
fields: IApprovalField[];
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
export interface IPartData {
|
|
2
15
|
toolCallId?: string;
|
|
3
16
|
toolName?: string;
|
|
@@ -9,7 +22,7 @@ export interface IPartData {
|
|
|
9
22
|
durationMs?: number;
|
|
10
23
|
toolInfo?: string;
|
|
11
24
|
status?: 'pending' | 'processing' | 'approved' | 'rejected';
|
|
12
|
-
|
|
25
|
+
approvals?: IApprovalDescription[];
|
|
13
26
|
error?: string;
|
|
14
27
|
}
|
|
15
28
|
export interface IPart {
|
|
@@ -24,11 +37,6 @@ export interface IFormattedToolCallPart {
|
|
|
24
37
|
toolInfo: IPartData;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
export interface IToolGroup {
|
|
28
|
-
title: string;
|
|
29
|
-
groupedTools: IFormattedToolCallPart[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
40
|
export interface IMessage {
|
|
33
41
|
id?: string;
|
|
34
42
|
role: 'user' | 'assistant' | 'system';
|
|
@@ -47,8 +55,8 @@ export interface IAgentSession {
|
|
|
47
55
|
title: string;
|
|
48
56
|
timestamp: string;
|
|
49
57
|
messages: IMessage[];
|
|
50
|
-
/** Tool calls this session is still waiting for the user to approve
|
|
51
|
-
pendingApprovals?:
|
|
58
|
+
/** Tool calls this session is still waiting for the user to approve. */
|
|
59
|
+
pendingApprovals?: IApprovalDescription[];
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
export interface ISessionsListItem {
|