@adminforth/agent 1.24.14 → 1.26.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.
@@ -93,7 +93,8 @@
93
93
  <div
94
94
  class="relative flex-1 min-h-0 flex flex-col overflow-hidden"
95
95
  >
96
- <ConversationArea
96
+ <ConversationArea
97
+ ref="conversationArea"
97
98
  v-if="agentStore.isChatOpen"
98
99
  :messages="agentStore.chatMessages"
99
100
  />
@@ -201,6 +202,7 @@ const props = defineProps<{
201
202
  const chatSurface = useTemplateRef('chatSurface');
202
203
  const textInput = useTemplateRef('textInput');
203
204
  const modeMenu = useTemplateRef('modeMenu');
205
+ const conversationArea = useTemplateRef('conversationArea');
204
206
  const agentStore = useAgentStore();
205
207
  const agentTransitions = useAgentTransitions();
206
208
  const coreStore = useCoreStore();
@@ -254,7 +256,6 @@ onMounted(async () => {
254
256
  agentStore.setIsTeleportedToBody(isTeleportedToBodyFromLocalStorage || props.meta.stickByDefault);
255
257
  }
256
258
  await agentStore.fetchSessionsList();
257
- agentStore.setFullScreen(true);
258
259
  });
259
260
 
260
261
  function autoResize() {
@@ -286,6 +287,7 @@ async function sendMessage() {
286
287
  isModeMenuOpen.value = false;
287
288
  await agentStore.sendMessage();
288
289
  autoResize();
290
+ conversationArea.value?.handleSendMessage();
289
291
  }
290
292
 
291
293
  </script>
@@ -2,7 +2,7 @@
2
2
  <CustomScrollbar
3
3
  ref="containerRef"
4
4
  class="auto-scroll-container mask-y"
5
- :wrapperStyle = "wrapperStyle"
5
+ :wrapperStyle = "wrapperStyle"
6
6
  :contentStyle = "contentStyle"
7
7
  @scroll="handleScroll"
8
8
  >
@@ -34,9 +34,10 @@
34
34
  >
35
35
 
36
36
  <div
37
- v-for="(message, index) in props.messages" :key="message.id"
37
+ v-for="(message, index) in props.messages" :key="index"
38
38
  class="flex flex-col w-full mt-2"
39
39
  :class="message.role === 'user' ? 'self-end' : 'self-start'"
40
+ ref="messagesRefs"
40
41
  >
41
42
  <MessageRenderer :message="message" :isLastMessageInChat="index === props.messages.length - 1"/>
42
43
  </div>
@@ -50,7 +51,7 @@
50
51
  <p>{{ $t('Start the conversation') }}</p>
51
52
  <p class="tracking-normal text-base text">{{ $t('Give any input to begin') }}</p>
52
53
  </div>
53
- <div></div>
54
+ <div v-if="showBottomSpacer" class="w-full" :style="{ height: spacerHeight + 'px' }"></div>
54
55
  </CustomAutoScrollContainer>
55
56
  <button @click="scrollContainer.scrollToBottom();">
56
57
  <IconArrowDownOutline
@@ -64,8 +65,8 @@
64
65
 
65
66
 
66
67
  <script setup lang="ts">
67
- import type { IMessage, IPart } from '../types';
68
- import { useTemplateRef, ref, defineAsyncComponent, onMounted, onUnmounted, watch, computed, nextTick } from 'vue';
68
+ import type { IMessage } from '../types';
69
+ import { useTemplateRef, ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
69
70
  import { IconArrowDownOutline } from '@iconify-prerendered/vue-flowbite';
70
71
  import SessionsHistory from '../SessionsHistory.vue';
71
72
  import { useAgentStore } from '../composables/useAgentStore';
@@ -77,18 +78,140 @@ const props = defineProps<{
77
78
  messages: IMessage[]
78
79
  }>();
79
80
 
81
+ defineExpose({
82
+ handleSendMessage
83
+ });
84
+
80
85
  const scrollContainer = useTemplateRef('scrollContainer');
81
86
  const showScrollToBottomButton = ref(false);
82
- const innerScrollContainerRef = ref(null);
87
+ const innerScrollContainerRef = ref<HTMLElement | null>(null);
83
88
  const agentStore = useAgentStore();
84
89
  const agentTransitions = useAgentTransitions();
85
90
  const showScrollContainer = ref(true);
86
- const chatContainerRef = ref(null);
91
+ const chatContainerRef = ref<HTMLElement | null>(null);
87
92
 
88
- const scrollHeight = computed(() => {
89
- return scrollContainer.value ? scrollContainer.value.scrollParams.scrollHeight : 0;
93
+ const messagesRefs = ref<Array<HTMLElement | null>>([]);
94
+ const showBottomSpacer = ref(false);
95
+ const spacerHeight = ref(0);
96
+ const MASK_HEIGHT = 20;
97
+ const EMPTY_MESSAGE_HEIGHT = 18;
98
+ let messageResizeObserver: ResizeObserver | null = null;
99
+ let observedLastUserMessageElement: HTMLElement | null = null;
100
+ let observedLastAgentMessageElement: HTMLElement | null = null;
101
+
102
+ function resetSpacer() {
103
+ showBottomSpacer.value = false;
104
+ spacerHeight.value = 0;
105
+ }
106
+
107
+ watch(() => agentStore.activeSessionId, () => {
108
+ resetSpacer();
90
109
  });
91
110
 
111
+ function getLastMessageElement(role: 'user' | 'assistant') {
112
+ const lastMessageIndex = props.messages.findLastIndex((message: IMessage) => message.role === role);
113
+ return messagesRefs.value[lastMessageIndex] ?? null;
114
+ }
115
+
116
+ function getHeightOfLastUserMessage() {
117
+ return getLastMessageElement('user')?.clientHeight ?? 0;
118
+ }
119
+
120
+ function getHeightOfLastAgentMessage() {
121
+ return getLastMessageElement('assistant')?.clientHeight ?? 0;
122
+ }
123
+
124
+ function getScrollClientHeight() {
125
+ return scrollContainer.value?.container.scrollEl.clientHeight ?? scrollContainer.value?.scrollParams.clientHeight ?? 0;
126
+ }
127
+
128
+ async function waitForRealHeight(role: 'user' | 'assistant'): Promise<number> {
129
+ return new Promise((resolve) => {
130
+ const interval = setInterval(() => {
131
+ const height = role === 'user' ? getHeightOfLastUserMessage() : getHeightOfLastAgentMessage();
132
+
133
+ if (height > EMPTY_MESSAGE_HEIGHT) {
134
+ clearInterval(interval);
135
+ resolve(height);
136
+ }
137
+ }, 50);
138
+ });
139
+ }
140
+
141
+ const useWaitingForHeight = ref(false);
142
+ async function updateSpacerHeight() {
143
+ if (!showBottomSpacer.value) {
144
+ return;
145
+ }
146
+
147
+ const clientHeight = getScrollClientHeight();
148
+
149
+ if (!clientHeight) {
150
+ return;
151
+ }
152
+
153
+ const lastUserMessageHeight = useWaitingForHeight.value ? await waitForRealHeight('user') : getHeightOfLastUserMessage();
154
+ const lastAgentMessageHeight = useWaitingForHeight.value ? await waitForRealHeight('assistant') : getHeightOfLastAgentMessage();
155
+
156
+ spacerHeight.value = Math.max(0, clientHeight - (lastUserMessageHeight + MASK_HEIGHT + lastAgentMessageHeight));
157
+ }
158
+
159
+ function stopObservingLastMessages() {
160
+ if (!messageResizeObserver) {
161
+ return;
162
+ }
163
+
164
+ if (observedLastUserMessageElement) {
165
+ messageResizeObserver.unobserve(observedLastUserMessageElement);
166
+ observedLastUserMessageElement = null;
167
+ }
168
+
169
+ if (observedLastAgentMessageElement) {
170
+ messageResizeObserver.unobserve(observedLastAgentMessageElement);
171
+ observedLastAgentMessageElement = null;
172
+ }
173
+ }
174
+
175
+ function observeLastMessages() {
176
+ if (!messageResizeObserver) {
177
+ return;
178
+ }
179
+
180
+ stopObservingLastMessages();
181
+
182
+ observedLastUserMessageElement = getLastMessageElement('user');
183
+ observedLastAgentMessageElement = getLastMessageElement('assistant');
184
+
185
+ if (observedLastUserMessageElement) {
186
+ messageResizeObserver.observe(observedLastUserMessageElement);
187
+ }
188
+
189
+ if (observedLastAgentMessageElement) {
190
+ messageResizeObserver.observe(observedLastAgentMessageElement);
191
+ }
192
+ }
193
+
194
+ async function refreshSpacerTracking() {
195
+ await nextTick();
196
+ observeLastMessages();
197
+ await updateSpacerHeight();
198
+ }
199
+
200
+ async function handleSendMessage() {
201
+ const clientHeight = getScrollClientHeight();
202
+
203
+ if (clientHeight) {
204
+ showBottomSpacer.value = true;
205
+ useWaitingForHeight.value = true;
206
+ setTimeout(() => {
207
+ useWaitingForHeight.value = false;
208
+ }, 1000);
209
+ await updateSpacerHeight();
210
+ await nextTick();
211
+ scrollContainer.value?.scrollToBottom();
212
+ }
213
+ }
214
+
92
215
  function recalculateScroll() {
93
216
  if (scrollContainer.value) {
94
217
  scrollContainer.value.handleScroll(false);
@@ -101,26 +224,44 @@ watch(() => agentStore.activeSessionId, async () => {
101
224
  showScrollContainer.value = false;
102
225
  await nextTick();
103
226
  showScrollContainer.value = true;
104
- await nextTick();
227
+ await refreshSpacerTracking();
105
228
  recalculateScroll();
106
229
  });
107
230
 
231
+ watch(() => props.messages.length, async () => {
232
+ await refreshSpacerTracking();
233
+ });
234
+
108
235
  onMounted(async () => {
236
+ messageResizeObserver = new ResizeObserver(() => {
237
+ updateSpacerHeight();
238
+ });
239
+
109
240
  await import('@incremark/theme/styles.css')
110
241
  await agentStore.fetchPlaceholderMessages()
242
+ await refreshSpacerTracking();
111
243
  });
112
244
 
113
245
  onUnmounted(() => {
246
+ if (innerScrollContainerRef.value) {
247
+ innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
248
+ }
249
+
250
+ stopObservingLastMessages();
251
+ messageResizeObserver?.disconnect();
114
252
  agentStore.stopPlaceholderAnimation();
115
253
  });
116
254
 
117
- watch(scrollContainer, () => {
255
+ watch(scrollContainer, async () => {
256
+ if (innerScrollContainerRef.value) {
257
+ innerScrollContainerRef.value.removeEventListener('scroll', recalculateScroll);
258
+ }
259
+
118
260
  if (scrollContainer.value) {
119
261
  innerScrollContainerRef.value = scrollContainer.value.container.scrollEl;
120
262
 
121
- innerScrollContainerRef.value.addEventListener('scroll', () => {
122
- recalculateScroll();
123
- });
263
+ innerScrollContainerRef.value.addEventListener('scroll', recalculateScroll);
264
+ await refreshSpacerTracking();
124
265
  }
125
266
  })
126
267
 
@@ -17,7 +17,7 @@
17
17
  :incremark-options="incremarkOptions"
18
18
  />
19
19
  <p v-else class="text-red-500 py-2">
20
- Error occured
20
+ Error occurred
21
21
  </p>
22
22
  </div>
23
23
  </template>
@@ -25,7 +25,6 @@ export interface IToolGroup {
25
25
  }
26
26
 
27
27
  export interface IMessage {
28
- id: string;
29
28
  role: 'user' | 'assistant';
30
29
  metadata?: any,
31
30
  parts: IPart[];
package/dist/index.js CHANGED
@@ -168,11 +168,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
168
168
  });
169
169
  }
170
170
  validateConfigAfterDiscover(adminforth, resourceConfig) {
171
- this.apiBasedTools = buildApiBasedTools(adminforth);
172
- for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
173
- assertRequiredApiTool(this.apiBasedTools, toolName);
174
- }
175
- assertRequiredApiTool(this.apiBasedTools, "update_record");
176
171
  this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth)
177
172
  .then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
178
173
  }
@@ -208,7 +203,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
208
203
  server.endpoint({
209
204
  method: 'POST',
210
205
  path: `/agent/response`,
211
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, _raw_express_res }) {
206
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, query, headers, cookies, adminUser, response, requestUrl, _raw_express_res }) {
212
207
  var _b, e_1, _c, _d;
213
208
  var _e, _f, _g;
214
209
  const res = _raw_express_res;
@@ -288,6 +283,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
288
283
  const selectedMode = (_g = this.options.modes.find((mode) => mode.name === body.mode)) !== null && _g !== void 0 ? _g : this.options.modes[0];
289
284
  const { model, summaryModel, modelMiddleware } = yield this.getModeModels(selectedMode, maxTokens);
290
285
  const systemPrompt = yield this.agentSystemPromptPromise;
286
+ const apiBasedTools = buildApiBasedTools(this.adminforth);
287
+ for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
288
+ assertRequiredApiTool(apiBasedTools, toolName);
289
+ }
290
+ assertRequiredApiTool(apiBasedTools, "update_record");
291
+ this.apiBasedTools = apiBasedTools;
291
292
  const stream = yield callAgent({
292
293
  name: `adminforth-agent-${this.pluginInstanceId}`,
293
294
  model,
@@ -300,10 +301,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
300
301
  ],
301
302
  adminUser,
302
303
  adminforth: this.adminforth,
303
- apiBasedTools: this.apiBasedTools,
304
+ apiBasedTools,
304
305
  customComponentsDir: this.adminforth.config.customization.customComponentsDir,
305
306
  sessionId,
306
307
  turnId,
308
+ httpExtra: {
309
+ body,
310
+ query,
311
+ headers,
312
+ cookies,
313
+ requestUrl,
314
+ response,
315
+ },
307
316
  userTimeZone,
308
317
  emitToolCallEvent,
309
318
  sequenceDebugSink: sequenceDebugCollector,
package/index.ts CHANGED
@@ -204,11 +204,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
204
204
  }
205
205
 
206
206
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
207
- this.apiBasedTools = buildApiBasedTools(adminforth);
208
- for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
209
- assertRequiredApiTool(this.apiBasedTools, toolName);
210
- }
211
- assertRequiredApiTool(this.apiBasedTools, "update_record");
212
207
  this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth)
213
208
  .then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
214
209
  }
@@ -248,7 +243,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
248
243
  server.endpoint({
249
244
  method: 'POST',
250
245
  path: `/agent/response`,
251
- handler: async ({ body, adminUser, _raw_express_res }) => {
246
+ handler: async ({ body, query, headers, cookies, adminUser, response, requestUrl, _raw_express_res }) => {
252
247
  const res = _raw_express_res;
253
248
  const messageId = randomUUID();
254
249
  const prompt = body.message;
@@ -346,6 +341,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
346
341
  const { model, summaryModel, modelMiddleware } =
347
342
  await this.getModeModels(selectedMode, maxTokens);
348
343
  const systemPrompt = await this.agentSystemPromptPromise;
344
+ const apiBasedTools = buildApiBasedTools(this.adminforth);
345
+ for (const toolName of ALWAYS_AVAILABLE_API_TOOL_NAMES) {
346
+ assertRequiredApiTool(apiBasedTools, toolName);
347
+ }
348
+ assertRequiredApiTool(apiBasedTools, "update_record");
349
+ this.apiBasedTools = apiBasedTools;
349
350
  const stream = await callAgent({
350
351
  name: `adminforth-agent-${this.pluginInstanceId}`,
351
352
  model,
@@ -358,10 +359,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
358
359
  ],
359
360
  adminUser,
360
361
  adminforth: this.adminforth,
361
- apiBasedTools: this.apiBasedTools,
362
+ apiBasedTools,
362
363
  customComponentsDir: this.adminforth.config.customization.customComponentsDir,
363
364
  sessionId,
364
365
  turnId,
366
+ httpExtra: {
367
+ body,
368
+ query,
369
+ headers,
370
+ cookies,
371
+ requestUrl,
372
+ response,
373
+ },
365
374
  userTimeZone,
366
375
  emitToolCallEvent,
367
376
  sequenceDebugSink: sequenceDebugCollector,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.24.14",
3
+ "version": "1.26.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",