@meistrari/chat-nuxt 1.11.0 → 1.13.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/dist/module.json +1 -1
- package/dist/module.mjs +15 -0
- package/dist/runtime/components/chat/conversation-item.d.vue.ts +6 -1
- package/dist/runtime/components/chat/conversation-item.vue +30 -141
- package/dist/runtime/components/chat/conversation-item.vue.d.ts +6 -1
- package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
- package/dist/runtime/components/chat/conversation-list.vue +300 -72
- package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
- package/dist/runtime/components/chat/conversation-new-group-button.d.vue.ts +7 -0
- package/dist/runtime/components/chat/conversation-new-group-button.vue +90 -0
- package/dist/runtime/components/chat/conversation-new-group-button.vue.d.ts +7 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.d.vue.ts +23 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.vue +75 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.vue.d.ts +23 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +2 -2
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +370 -41
- package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +2 -2
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +6 -6
- package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +6 -6
- package/dist/runtime/components/chat/reasoning-message.vue +4 -1
- package/dist/runtime/components/chat/topbar.d.vue.ts +2 -2
- package/dist/runtime/components/chat/topbar.vue.d.ts +2 -2
- package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
- package/dist/runtime/composables/useConversationGroups.js +160 -0
- package/dist/runtime/composables/useConversationItem.d.ts +48 -0
- package/dist/runtime/composables/useConversationItem.js +176 -0
- package/dist/runtime/composables/useConversationList.d.ts +128 -0
- package/dist/runtime/composables/useConversationList.js +553 -0
- package/dist/runtime/composables/useConversations.d.ts +2 -0
- package/dist/runtime/composables/useConversations.js +45 -1
- package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
- package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
- package/dist/runtime/composables/useVerbConjugation.d.ts +1 -0
- package/dist/runtime/composables/useVerbConjugation.js +14 -1
- package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
- package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
- package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
- package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
- package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
- package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +2 -0
- package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +26 -4
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
- package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
- package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
- package/dist/runtime/server/api/conversations/index.post.js +1 -0
- package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
- package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
- package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
- package/dist/runtime/server/db/schema/conversations.js +3 -0
- package/dist/runtime/server/db/schema/index.d.ts +1 -0
- package/dist/runtime/server/db/schema/index.js +1 -0
- package/dist/runtime/server/db/schema/messages.d.ts +34 -0
- package/dist/runtime/server/db/schema/messages.js +3 -1
- package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
- package/dist/runtime/server/utils/conversation-group.js +48 -0
- package/dist/runtime/server/utils/conversation-message-sync.d.ts +1 -1
- package/dist/runtime/server/utils/conversation-message-sync.js +2 -2
- package/dist/runtime/server/utils/tela-agent-api.d.ts +3 -1
- package/dist/runtime/server/utils/tela-agent-api.js +285 -26
- package/dist/runtime/server/utils/tela-agent-session.d.ts +14 -4
- package/dist/runtime/server/utils/tela-agent-session.js +57 -22
- package/dist/runtime/types/chat.d.ts +2 -1
- package/dist/runtime/types/tela-agent.d.ts +155 -43
- package/dist/runtime/types/tela-agent.js +49 -11
- package/dist/runtime/utils/conversation-groups.d.ts +18 -0
- package/dist/runtime/utils/conversation-groups.js +30 -0
- package/drizzle/0016_dry_aaron_stack.sql +16 -0
- package/drizzle/0017_typical_dexter_bennett.sql +2 -0
- package/drizzle/meta/0016_snapshot.json +887 -0
- package/drizzle/meta/0017_snapshot.json +899 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { CHAT_SCHEMA_NAME, chatSchema } from './_schema.js';
|
|
2
|
+
export { conversationGroups } from './conversation-groups.js';
|
|
2
3
|
export { conversationUsage, type ConversationUsageMetadata, type ModelUsageEntry } from './conversation-usage.js';
|
|
3
4
|
export { conversations } from './conversations.js';
|
|
4
5
|
export { type ExternalSkillInsert, type ExternalSkillRow, externalSkills, externalSkillsRelations } from './external-skills.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { CHAT_SCHEMA_NAME, chatSchema } from "./_schema.js";
|
|
2
|
+
export { conversationGroups } from "./conversation-groups.js";
|
|
2
3
|
export { conversationUsage } from "./conversation-usage.js";
|
|
3
4
|
export { conversations } from "./conversations.js";
|
|
4
5
|
export { externalSkills, externalSkillsRelations } from "./external-skills.js";
|
|
@@ -148,6 +148,40 @@ export declare const messages: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
148
148
|
identity: undefined;
|
|
149
149
|
generated: undefined;
|
|
150
150
|
}, {}, {}>;
|
|
151
|
+
agentSessionCursor: import("drizzle-orm/pg-core").PgColumn<{
|
|
152
|
+
name: "agent_session_cursor";
|
|
153
|
+
tableName: "messages";
|
|
154
|
+
dataType: "number";
|
|
155
|
+
columnType: "PgInteger";
|
|
156
|
+
data: number;
|
|
157
|
+
driverParam: string | number;
|
|
158
|
+
notNull: false;
|
|
159
|
+
hasDefault: false;
|
|
160
|
+
isPrimaryKey: false;
|
|
161
|
+
isAutoincrement: false;
|
|
162
|
+
hasRuntimeDefault: false;
|
|
163
|
+
enumValues: undefined;
|
|
164
|
+
baseColumn: never;
|
|
165
|
+
identity: undefined;
|
|
166
|
+
generated: undefined;
|
|
167
|
+
}, {}, {}>;
|
|
168
|
+
agentSessionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
169
|
+
name: "agent_session_id";
|
|
170
|
+
tableName: "messages";
|
|
171
|
+
dataType: "string";
|
|
172
|
+
columnType: "PgText";
|
|
173
|
+
data: string;
|
|
174
|
+
driverParam: string;
|
|
175
|
+
notNull: false;
|
|
176
|
+
hasDefault: false;
|
|
177
|
+
isPrimaryKey: false;
|
|
178
|
+
isAutoincrement: false;
|
|
179
|
+
hasRuntimeDefault: false;
|
|
180
|
+
enumValues: [string, ...string[]];
|
|
181
|
+
baseColumn: never;
|
|
182
|
+
identity: undefined;
|
|
183
|
+
generated: undefined;
|
|
184
|
+
}, {}, {}>;
|
|
151
185
|
ttft: import("drizzle-orm/pg-core").PgColumn<{
|
|
152
186
|
name: "ttft";
|
|
153
187
|
tableName: "messages";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { uuid, text, timestamp, index, jsonb } from "drizzle-orm/pg-core";
|
|
1
|
+
import { uuid, text, timestamp, index, integer, jsonb } from "drizzle-orm/pg-core";
|
|
2
2
|
import { chatSchema } from "./_schema.js";
|
|
3
3
|
import { conversations } from "./conversations.js";
|
|
4
4
|
export const messages = chatSchema.table("messages", {
|
|
@@ -10,6 +10,8 @@ export const messages = chatSchema.table("messages", {
|
|
|
10
10
|
reasoningData: jsonb("reasoning_data"),
|
|
11
11
|
files: jsonb("files").$type(),
|
|
12
12
|
externalUuid: text("external_uuid"),
|
|
13
|
+
agentSessionCursor: integer("agent_session_cursor"),
|
|
14
|
+
agentSessionId: text("agent_session_id"),
|
|
13
15
|
ttft: text("ttft"),
|
|
14
16
|
createdBy: text("created_by"),
|
|
15
17
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
|
|
2
|
+
export declare function conversationGroupScopeConditions(context: ChatRuntimeContext, groupId?: string): import("drizzle-orm").SQL<unknown> | undefined;
|
|
3
|
+
export declare function resolveConversationGroupName(name: unknown): string;
|
|
4
|
+
export declare function resolveOptionalConversationGroupId(groupId: unknown): string | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
2
|
+
import { createError } from "h3";
|
|
3
|
+
import { schema } from "#chat-runtime/server/db";
|
|
4
|
+
const MAX_GROUP_NAME_LENGTH = 80;
|
|
5
|
+
export function conversationGroupScopeConditions(context, groupId) {
|
|
6
|
+
const conditions = [
|
|
7
|
+
eq(schema.conversationGroups.workspaceId, context.workspaceId),
|
|
8
|
+
context.telaAgentId ? eq(schema.conversationGroups.telaAgentId, context.telaAgentId) : isNull(schema.conversationGroups.telaAgentId),
|
|
9
|
+
context.conversationScope ? eq(schema.conversationGroups.conversationScope, context.conversationScope) : isNull(schema.conversationGroups.conversationScope)
|
|
10
|
+
];
|
|
11
|
+
if (groupId) {
|
|
12
|
+
conditions.unshift(eq(schema.conversationGroups.id, groupId));
|
|
13
|
+
}
|
|
14
|
+
return and(...conditions);
|
|
15
|
+
}
|
|
16
|
+
export function resolveConversationGroupName(name) {
|
|
17
|
+
if (typeof name !== "string") {
|
|
18
|
+
throw createError({
|
|
19
|
+
statusCode: 400,
|
|
20
|
+
statusMessage: "Group name is required"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const trimmedName = name.trim();
|
|
24
|
+
if (!trimmedName) {
|
|
25
|
+
throw createError({
|
|
26
|
+
statusCode: 400,
|
|
27
|
+
statusMessage: "Group name cannot be empty"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (trimmedName.length > MAX_GROUP_NAME_LENGTH) {
|
|
31
|
+
throw createError({
|
|
32
|
+
statusCode: 400,
|
|
33
|
+
statusMessage: "Group name is too long"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return trimmedName;
|
|
37
|
+
}
|
|
38
|
+
export function resolveOptionalConversationGroupId(groupId) {
|
|
39
|
+
if (groupId === null)
|
|
40
|
+
return null;
|
|
41
|
+
if (typeof groupId !== "string" || !groupId.trim()) {
|
|
42
|
+
throw createError({
|
|
43
|
+
statusCode: 400,
|
|
44
|
+
statusMessage: "Invalid group ID"
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return groupId.trim();
|
|
48
|
+
}
|
|
@@ -20,7 +20,7 @@ type MessageUpdateResponse = {
|
|
|
20
20
|
export declare const PENDING_RESPONSE_GRACE_MS = 30000;
|
|
21
21
|
export declare const THREAD_SESSION_START_ERROR = "Erro: N\u00E3o foi poss\u00EDvel iniciar a sess\u00E3o de resposta. Tente enviar a mensagem novamente.";
|
|
22
22
|
export declare const SESSION_STATUS_CHECK_ERROR = "Erro: N\u00E3o foi poss\u00EDvel verificar o andamento da resposta. Tente enviar a mensagem novamente.";
|
|
23
|
-
export declare function hasPendingMessageExceededGraceWindow(pendingMessage: Pick<Message, 'createdAt'>, now?: Date): boolean;
|
|
23
|
+
export declare function hasPendingMessageExceededGraceWindow(pendingMessage: Pick<Message, 'createdAt'>, now?: Date, graceMs?: number): boolean;
|
|
24
24
|
export declare function buildFailedPendingMessageUpdate(content: string, now?: Date): MessageUpdate;
|
|
25
25
|
export declare function mapAgentApiSessionToMessageUpdate(agentSession: AgentApiSession, options: {
|
|
26
26
|
pendingMessage: Pick<Message, 'ttft' | 'createdAt' | 'updatedAt'>;
|
|
@@ -24,8 +24,8 @@ function resolveTerminalContentForPendingMessage(agentSession, newReasoning, las
|
|
|
24
24
|
return null;
|
|
25
25
|
return sessionContent || reasoningContent;
|
|
26
26
|
}
|
|
27
|
-
export function hasPendingMessageExceededGraceWindow(pendingMessage, now = /* @__PURE__ */ new Date()) {
|
|
28
|
-
return now.getTime() - pendingMessage.createdAt.getTime() >=
|
|
27
|
+
export function hasPendingMessageExceededGraceWindow(pendingMessage, now = /* @__PURE__ */ new Date(), graceMs = PENDING_RESPONSE_GRACE_MS) {
|
|
28
|
+
return now.getTime() - pendingMessage.createdAt.getTime() >= graceMs;
|
|
29
29
|
}
|
|
30
30
|
export function buildFailedPendingMessageUpdate(content, now = /* @__PURE__ */ new Date()) {
|
|
31
31
|
return {
|
|
@@ -14,7 +14,9 @@ export declare function getTelaAgent(event: H3Event, agentId: string): Promise<{
|
|
|
14
14
|
success: false;
|
|
15
15
|
error: string;
|
|
16
16
|
}>;
|
|
17
|
-
export declare function getTelaAgentSession(event: H3Event, sessionId: string
|
|
17
|
+
export declare function getTelaAgentSession(event: H3Event, sessionId: string, options?: {
|
|
18
|
+
cursor?: number | null;
|
|
19
|
+
}): Promise<{
|
|
18
20
|
success: true;
|
|
19
21
|
data: TelaAgentSession;
|
|
20
22
|
} | {
|
|
@@ -3,10 +3,14 @@ import {
|
|
|
3
3
|
telaAgentApiResponseSchema,
|
|
4
4
|
telaAgentEndSessionApiResponseSchema,
|
|
5
5
|
telaAgentRunApiResponseSchema,
|
|
6
|
-
|
|
6
|
+
telaAgentV4SessionStreamEventSchema
|
|
7
7
|
} from "#chat-runtime/types/tela-agent";
|
|
8
8
|
import { getAgentDataToken } from "#chat-runtime/server/utils/agent-api";
|
|
9
9
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
10
|
+
const TELA_SESSION_STREAM_FIRST_EVENT_TIMEOUT_MS = 3e3;
|
|
11
|
+
const TELA_SESSION_STREAM_IDLE_TIMEOUT_MS = 250;
|
|
12
|
+
const TELA_AGENT_V4_SESSION_EVENT_KINDS = /* @__PURE__ */ new Set(["status", "steps", "result", "error"]);
|
|
13
|
+
const TELA_AGENT_METADATA_MAX_ATTEMPTS = 3;
|
|
10
14
|
function getTelaErrorMessage(body, fallback) {
|
|
11
15
|
if (body && typeof body === "object") {
|
|
12
16
|
const record = body;
|
|
@@ -27,26 +31,105 @@ async function readResponseBody(response) {
|
|
|
27
31
|
return text;
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
|
-
|
|
34
|
+
function getErrorMessage(error) {
|
|
35
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
36
|
+
}
|
|
37
|
+
async function telaAgentFetch(event, path, init, options = {}) {
|
|
31
38
|
const { telaApiUrl } = useRuntimeConfig();
|
|
32
39
|
const token = getAgentDataToken(event);
|
|
33
40
|
const url = `${telaApiUrl}${path}`;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const maxAttempts = options.maxAttempts ?? 1;
|
|
42
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
43
|
+
let response;
|
|
44
|
+
let body;
|
|
45
|
+
try {
|
|
46
|
+
response = await fetch(url, {
|
|
47
|
+
...init,
|
|
48
|
+
headers: {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
"x-data-token": token,
|
|
51
|
+
"Authorization": `Bearer ${token}`,
|
|
52
|
+
...init.headers
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
body = await readResponseBody(response);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const message = getErrorMessage(error);
|
|
58
|
+
logger.warn({ error: message, path }, "Tela agent API request threw");
|
|
59
|
+
return { success: false, error: `Tela API request failed: ${message}` };
|
|
41
60
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const error = getTelaErrorMessage(body, `Tela API error: ${response.status} - ${response.statusText}`);
|
|
63
|
+
if (attempt < maxAttempts && options.retryStatusCodes?.includes(response.status)) {
|
|
64
|
+
logger.warn({ status: response.status, statusText: response.statusText, path, attempt }, "Tela agent API request failed, retrying");
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
logger.warn({ status: response.status, statusText: response.statusText, path }, "Tela agent API request failed");
|
|
68
|
+
return { success: false, error };
|
|
69
|
+
}
|
|
70
|
+
return { success: true, body };
|
|
71
|
+
}
|
|
72
|
+
return { success: false, error: "Tela API request failed" };
|
|
73
|
+
}
|
|
74
|
+
function getTelaAgentV4SessionEventKind(event) {
|
|
75
|
+
if (!event || typeof event !== "object" || !("kind" in event))
|
|
76
|
+
return null;
|
|
77
|
+
const kind = event.kind;
|
|
78
|
+
return typeof kind === "string" ? kind : null;
|
|
79
|
+
}
|
|
80
|
+
function parseTelaAgentV4SessionSseFrame(frame) {
|
|
81
|
+
let data = "";
|
|
82
|
+
let hasData = false;
|
|
83
|
+
let lineStart = 0;
|
|
84
|
+
while (lineStart <= frame.length) {
|
|
85
|
+
const lineEnd = frame.indexOf("\n", lineStart);
|
|
86
|
+
const line = lineEnd === -1 ? frame.slice(lineStart) : frame.slice(lineStart, lineEnd);
|
|
87
|
+
lineStart = lineEnd === -1 ? frame.length + 1 : lineEnd + 1;
|
|
88
|
+
if (!line || line.startsWith(":"))
|
|
89
|
+
continue;
|
|
90
|
+
const separatorIndex = line.indexOf(":");
|
|
91
|
+
const field = separatorIndex === -1 ? line : line.slice(0, separatorIndex);
|
|
92
|
+
let value = separatorIndex === -1 ? "" : line.slice(separatorIndex + 1);
|
|
93
|
+
if (value.startsWith(" "))
|
|
94
|
+
value = value.slice(1);
|
|
95
|
+
if (field === "data") {
|
|
96
|
+
data = hasData ? `${data}
|
|
97
|
+
${value}` : value;
|
|
98
|
+
hasData = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!hasData || !data || data === "[DONE]" || data === "Stream complete")
|
|
102
|
+
return void 0;
|
|
103
|
+
let rawEvent;
|
|
104
|
+
try {
|
|
105
|
+
rawEvent = JSON.parse(data);
|
|
106
|
+
} catch {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
const eventKind = getTelaAgentV4SessionEventKind(rawEvent);
|
|
110
|
+
if (!eventKind || !TELA_AGENT_V4_SESSION_EVENT_KINDS.has(eventKind))
|
|
111
|
+
return void 0;
|
|
112
|
+
const parsed = telaAgentV4SessionStreamEventSchema.safeParse(rawEvent);
|
|
113
|
+
if (!parsed.success)
|
|
114
|
+
return void 0;
|
|
115
|
+
return parsed.data;
|
|
116
|
+
}
|
|
117
|
+
async function readTelaSessionStreamChunk(reader, timeoutMs) {
|
|
118
|
+
let timeout;
|
|
119
|
+
try {
|
|
120
|
+
return await Promise.race([
|
|
121
|
+
reader.read().then(
|
|
122
|
+
(result) => ({ type: "read", result }),
|
|
123
|
+
(error) => ({ type: "error", error })
|
|
124
|
+
),
|
|
125
|
+
new Promise((resolve) => {
|
|
126
|
+
timeout = setTimeout(() => resolve({ type: "timeout" }), timeoutMs);
|
|
127
|
+
})
|
|
128
|
+
]);
|
|
129
|
+
} finally {
|
|
130
|
+
if (timeout)
|
|
131
|
+
clearTimeout(timeout);
|
|
48
132
|
}
|
|
49
|
-
return { success: true, body };
|
|
50
133
|
}
|
|
51
134
|
export async function runTelaAgent(event, agentId, payload) {
|
|
52
135
|
const encodedAgentId = encodeURIComponent(agentId);
|
|
@@ -67,6 +150,9 @@ export async function getTelaAgent(event, agentId) {
|
|
|
67
150
|
const encodedAgentId = encodeURIComponent(agentId);
|
|
68
151
|
const result = await telaAgentFetch(event, `/agent/${encodedAgentId}`, {
|
|
69
152
|
method: "GET"
|
|
153
|
+
}, {
|
|
154
|
+
maxAttempts: TELA_AGENT_METADATA_MAX_ATTEMPTS,
|
|
155
|
+
retryStatusCodes: [502]
|
|
70
156
|
});
|
|
71
157
|
if (!result.success)
|
|
72
158
|
return result;
|
|
@@ -77,19 +163,192 @@ export async function getTelaAgent(event, agentId) {
|
|
|
77
163
|
}
|
|
78
164
|
return { success: true, data: parsed.data.data };
|
|
79
165
|
}
|
|
80
|
-
export async function getTelaAgentSession(event, sessionId) {
|
|
166
|
+
export async function getTelaAgentSession(event, sessionId, options = {}) {
|
|
167
|
+
const { telaApiUrl } = useRuntimeConfig();
|
|
168
|
+
const token = getAgentDataToken(event);
|
|
81
169
|
const encodedSessionId = encodeURIComponent(sessionId);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
170
|
+
const params = new URLSearchParams();
|
|
171
|
+
if (typeof options.cursor === "number")
|
|
172
|
+
params.set("cursor", String(options.cursor));
|
|
173
|
+
const queryString = params.toString();
|
|
174
|
+
const path = `/agent/sessions/${encodedSessionId}${queryString ? `?${queryString}` : ""}`;
|
|
175
|
+
const url = `${telaApiUrl}${path}`;
|
|
176
|
+
const abortController = new AbortController();
|
|
177
|
+
let completed = false;
|
|
178
|
+
let response = null;
|
|
179
|
+
let reader = null;
|
|
180
|
+
const abortOnResponseClose = () => {
|
|
181
|
+
if (!completed)
|
|
182
|
+
abortController.abort();
|
|
183
|
+
};
|
|
184
|
+
event.node?.res?.once?.("close", abortOnResponseClose);
|
|
185
|
+
try {
|
|
186
|
+
let buildSnapshot = function() {
|
|
187
|
+
if (!status)
|
|
188
|
+
return null;
|
|
189
|
+
return {
|
|
190
|
+
sessionId,
|
|
191
|
+
status,
|
|
192
|
+
steps,
|
|
193
|
+
...nextCursor !== void 0 ? { nextCursor } : {},
|
|
194
|
+
...result ? { result } : {},
|
|
195
|
+
...error ? { error } : {},
|
|
196
|
+
...createdAt !== void 0 ? { createdAt } : {},
|
|
197
|
+
...updatedAt !== void 0 ? { updatedAt } : {}
|
|
198
|
+
};
|
|
199
|
+
}, applyStreamEvent = function(streamEvent) {
|
|
200
|
+
if (streamEvent.kind === "error") {
|
|
201
|
+
status = "failed";
|
|
202
|
+
error = streamEvent.error;
|
|
203
|
+
return buildSnapshot();
|
|
204
|
+
}
|
|
205
|
+
status = streamEvent.status;
|
|
206
|
+
if (streamEvent.kind === "status") {
|
|
207
|
+
error = streamEvent.error;
|
|
208
|
+
createdAt = streamEvent.createdAt;
|
|
209
|
+
updatedAt = streamEvent.updatedAt;
|
|
210
|
+
if (status === "failed" || status === "cancelled")
|
|
211
|
+
return buildSnapshot();
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
createdAt = streamEvent.createdAt;
|
|
215
|
+
updatedAt = streamEvent.updatedAt;
|
|
216
|
+
nextCursor = streamEvent.nextCursor;
|
|
217
|
+
if (streamEvent.kind === "steps") {
|
|
218
|
+
for (const step of streamEvent.steps) {
|
|
219
|
+
const key = step.eventId ?? step.id;
|
|
220
|
+
if (!key)
|
|
221
|
+
continue;
|
|
222
|
+
const existingIndex = stepIndexByKey.get(key);
|
|
223
|
+
if (existingIndex === void 0) {
|
|
224
|
+
stepIndexByKey.set(key, steps.length);
|
|
225
|
+
steps.push(step);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
steps[existingIndex] = {
|
|
229
|
+
...steps[existingIndex],
|
|
230
|
+
...step
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (status === "failed" || status === "cancelled")
|
|
234
|
+
return buildSnapshot();
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
result = streamEvent.result;
|
|
238
|
+
error = void 0;
|
|
239
|
+
return buildSnapshot();
|
|
240
|
+
}, drainBuffer = function() {
|
|
241
|
+
while (true) {
|
|
242
|
+
const frameBoundary = buffer.indexOf("\n\n");
|
|
243
|
+
if (frameBoundary === -1)
|
|
244
|
+
return null;
|
|
245
|
+
const frame = buffer.slice(0, frameBoundary);
|
|
246
|
+
buffer = buffer.slice(frameBoundary + 2);
|
|
247
|
+
const streamEvent = parseTelaAgentV4SessionSseFrame(frame);
|
|
248
|
+
if (!streamEvent)
|
|
249
|
+
continue;
|
|
250
|
+
const terminalSnapshot = applyStreamEvent(streamEvent);
|
|
251
|
+
if (terminalSnapshot)
|
|
252
|
+
return terminalSnapshot;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
response = await fetch(url, {
|
|
257
|
+
method: "GET",
|
|
258
|
+
signal: abortController.signal,
|
|
259
|
+
headers: {
|
|
260
|
+
"Accept": "text/event-stream",
|
|
261
|
+
"x-data-token": token,
|
|
262
|
+
"Authorization": `Bearer ${token}`
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
} catch (error2) {
|
|
266
|
+
const message = getErrorMessage(error2);
|
|
267
|
+
logger.warn({ error: message, path }, "Tela agent API request threw");
|
|
268
|
+
return { success: false, error: `Tela API request failed: ${message}` };
|
|
269
|
+
}
|
|
270
|
+
if (!response.ok) {
|
|
271
|
+
let body;
|
|
272
|
+
try {
|
|
273
|
+
body = await readResponseBody(response);
|
|
274
|
+
} catch (error3) {
|
|
275
|
+
const message = getErrorMessage(error3);
|
|
276
|
+
logger.warn({ error: message, path }, "Tela agent API error response read failed");
|
|
277
|
+
return { success: false, error: `Tela API request failed: ${message}` };
|
|
278
|
+
}
|
|
279
|
+
const error2 = getTelaErrorMessage(body, `Tela API error: ${response.status} - ${response.statusText}`);
|
|
280
|
+
logger.warn({ status: response.status, statusText: response.statusText, path }, "Tela agent API request failed");
|
|
281
|
+
return { success: false, error: error2 };
|
|
282
|
+
}
|
|
283
|
+
if (!response.body)
|
|
284
|
+
return { success: false, error: "Tela session stream response body is missing" };
|
|
285
|
+
reader = response.body.getReader();
|
|
286
|
+
const decoder = new TextDecoder();
|
|
287
|
+
const steps = [];
|
|
288
|
+
const stepIndexByKey = /* @__PURE__ */ new Map();
|
|
289
|
+
let status = null;
|
|
290
|
+
let nextCursor;
|
|
291
|
+
let result;
|
|
292
|
+
let error;
|
|
293
|
+
let createdAt;
|
|
294
|
+
let updatedAt;
|
|
295
|
+
let buffer = "";
|
|
296
|
+
while (true) {
|
|
297
|
+
const snapshot = buildSnapshot();
|
|
298
|
+
const readResult = await readTelaSessionStreamChunk(
|
|
299
|
+
reader,
|
|
300
|
+
snapshot ? TELA_SESSION_STREAM_IDLE_TIMEOUT_MS : TELA_SESSION_STREAM_FIRST_EVENT_TIMEOUT_MS
|
|
301
|
+
);
|
|
302
|
+
if (readResult.type === "timeout") {
|
|
303
|
+
if (snapshot)
|
|
304
|
+
return { success: true, data: snapshot };
|
|
305
|
+
return { success: false, error: "Tela session stream timed out before a session event" };
|
|
306
|
+
}
|
|
307
|
+
if (readResult.type === "error") {
|
|
308
|
+
const message = getErrorMessage(readResult.error);
|
|
309
|
+
const activeSnapshot = buildSnapshot();
|
|
310
|
+
if (activeSnapshot) {
|
|
311
|
+
logger.warn({ error: message, path, sessionId }, "Tela agent session stream failed after active snapshot");
|
|
312
|
+
return { success: true, data: activeSnapshot };
|
|
313
|
+
}
|
|
314
|
+
logger.warn({ error: message, path }, "Tela agent API request threw");
|
|
315
|
+
return { success: false, error: `Tela API request failed: ${message}` };
|
|
316
|
+
}
|
|
317
|
+
const { done, value } = readResult.result;
|
|
318
|
+
if (done) {
|
|
319
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
320
|
+
if (buffer.trim()) {
|
|
321
|
+
const streamEvent = parseTelaAgentV4SessionSseFrame(buffer);
|
|
322
|
+
if (streamEvent) {
|
|
323
|
+
const terminalSnapshot2 = applyStreamEvent(streamEvent);
|
|
324
|
+
if (terminalSnapshot2)
|
|
325
|
+
return { success: true, data: terminalSnapshot2 };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const snapshot2 = buildSnapshot();
|
|
329
|
+
if (snapshot2)
|
|
330
|
+
return { success: true, data: snapshot2 };
|
|
331
|
+
return { success: false, error: "Tela session stream ended without a session event" };
|
|
332
|
+
}
|
|
333
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
334
|
+
const terminalSnapshot = drainBuffer();
|
|
335
|
+
if (terminalSnapshot)
|
|
336
|
+
return { success: true, data: terminalSnapshot };
|
|
337
|
+
}
|
|
338
|
+
} catch (error) {
|
|
339
|
+
const message = getErrorMessage(error);
|
|
340
|
+
logger.warn({ error: message, sessionId }, "Tela agent session response parse error");
|
|
90
341
|
return { success: false, error: "Invalid session response from Tela API" };
|
|
342
|
+
} finally {
|
|
343
|
+
completed = true;
|
|
344
|
+
event.node?.res?.off?.("close", abortOnResponseClose);
|
|
345
|
+
abortController.abort();
|
|
346
|
+
await reader?.cancel().catch(() => void 0);
|
|
347
|
+
try {
|
|
348
|
+
reader?.releaseLock();
|
|
349
|
+
} catch {
|
|
350
|
+
}
|
|
91
351
|
}
|
|
92
|
-
return { success: true, data: parsed.data.data };
|
|
93
352
|
}
|
|
94
353
|
export async function endTelaAgentSession(event, sessionId) {
|
|
95
354
|
const encodedSessionId = encodeURIComponent(sessionId);
|
|
@@ -4,6 +4,8 @@ import type { TelaAgentExecutionInput, TelaAgentInputVariable, TelaAgentSession,
|
|
|
4
4
|
export type TelaAgentMessageUpdate = {
|
|
5
5
|
reasoningData?: ReasoningItem[];
|
|
6
6
|
externalUuid?: string;
|
|
7
|
+
agentSessionCursor?: number;
|
|
8
|
+
agentSessionId?: string;
|
|
7
9
|
content?: string;
|
|
8
10
|
status?: MessageStatus;
|
|
9
11
|
ttft?: string;
|
|
@@ -33,12 +35,20 @@ export declare function toTelaAgentAttachments(files: TelaAgentFileInput[] | und
|
|
|
33
35
|
vaultRef: string;
|
|
34
36
|
filename: any;
|
|
35
37
|
}[];
|
|
36
|
-
export declare function getTelaAgentStepSyncId(step: TelaAgentStep): string;
|
|
38
|
+
export declare function getTelaAgentStepSyncId(step: TelaAgentStep): string | null;
|
|
37
39
|
export declare function normalizeTelaAgentReasoningStep(sessionId: string, step: TelaAgentStep): ReasoningItem | null;
|
|
38
|
-
export declare function
|
|
39
|
-
export declare function
|
|
40
|
+
export declare function extractTelaAgentAssistantContent(session: TelaAgentSession): string;
|
|
41
|
+
export declare function resolveTelaAgentRequestCursor(options: {
|
|
42
|
+
pendingMessage: {
|
|
43
|
+
agentSessionCursor?: number | null;
|
|
44
|
+
};
|
|
45
|
+
lastTerminalMessage?: {
|
|
46
|
+
agentSessionCursor?: number | null;
|
|
47
|
+
agentSessionId?: string | null;
|
|
48
|
+
} | null;
|
|
49
|
+
threadSessionId: string;
|
|
50
|
+
}): number | undefined;
|
|
40
51
|
export declare function mapTelaAgentSessionToMessageUpdate(session: TelaAgentSession, options?: {
|
|
41
|
-
lastSyncedStepId?: string | null;
|
|
42
52
|
existingReasoningData?: ReasoningItem[] | null;
|
|
43
53
|
pendingMessageTtft?: string | null;
|
|
44
54
|
userMessageCreatedAt?: Date | null;
|
|
@@ -43,7 +43,7 @@ export function toTelaAgentAttachments(files) {
|
|
|
43
43
|
}));
|
|
44
44
|
}
|
|
45
45
|
export function getTelaAgentStepSyncId(step) {
|
|
46
|
-
return step.eventId ?? step.id;
|
|
46
|
+
return step.eventId ?? step.id ?? null;
|
|
47
47
|
}
|
|
48
48
|
function isRecord(value) {
|
|
49
49
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -251,6 +251,9 @@ function getStepText(step) {
|
|
|
251
251
|
return null;
|
|
252
252
|
}
|
|
253
253
|
export function normalizeTelaAgentReasoningStep(sessionId, step) {
|
|
254
|
+
const syncId = getTelaAgentStepSyncId(step);
|
|
255
|
+
if (!syncId)
|
|
256
|
+
return null;
|
|
254
257
|
const content = normalizeStepMessageContent(step);
|
|
255
258
|
if (content.length === 0)
|
|
256
259
|
return null;
|
|
@@ -264,7 +267,7 @@ export function normalizeTelaAgentReasoningStep(sessionId, step) {
|
|
|
264
267
|
const parentToolUseId = getStringProperty(record, "parent_tool_use_id");
|
|
265
268
|
return {
|
|
266
269
|
type,
|
|
267
|
-
uuid:
|
|
270
|
+
uuid: syncId,
|
|
268
271
|
message: {
|
|
269
272
|
role: role ?? (type === "user" ? "user" : "assistant"),
|
|
270
273
|
content
|
|
@@ -275,40 +278,70 @@ export function normalizeTelaAgentReasoningStep(sessionId, step) {
|
|
|
275
278
|
parent_tool_use_id: parentToolUseId
|
|
276
279
|
};
|
|
277
280
|
}
|
|
278
|
-
export function
|
|
279
|
-
if (!lastSyncedStepId)
|
|
280
|
-
return session.steps;
|
|
281
|
-
const lastSyncedIndex = session.steps.findIndex((step) => getTelaAgentStepSyncId(step) === lastSyncedStepId);
|
|
282
|
-
return session.steps.slice(lastSyncedIndex + 1);
|
|
283
|
-
}
|
|
284
|
-
export function extractTelaAgentAssistantContent(session, steps = session.steps) {
|
|
281
|
+
export function extractTelaAgentAssistantContent(session) {
|
|
285
282
|
const structuredContent = session.result?.structuredContent;
|
|
286
283
|
if (structuredContent)
|
|
287
284
|
return sanitizeText(jsonMarkdown(structuredContent));
|
|
288
285
|
if (session.result?.content)
|
|
289
286
|
return normalizeTelaAgentResultContent(session.result.content);
|
|
290
|
-
for (let i = steps.length - 1; i >= 0; i--) {
|
|
291
|
-
const text = getStepText(steps[i]);
|
|
287
|
+
for (let i = session.steps.length - 1; i >= 0; i--) {
|
|
288
|
+
const text = getStepText(session.steps[i]);
|
|
292
289
|
if (text)
|
|
293
290
|
return text;
|
|
294
291
|
}
|
|
295
292
|
return "";
|
|
296
293
|
}
|
|
294
|
+
export function resolveTelaAgentRequestCursor(options) {
|
|
295
|
+
if (typeof options.pendingMessage.agentSessionCursor === "number")
|
|
296
|
+
return options.pendingMessage.agentSessionCursor;
|
|
297
|
+
const lastTerminalMessage = options.lastTerminalMessage;
|
|
298
|
+
if (lastTerminalMessage && lastTerminalMessage.agentSessionId === options.threadSessionId && typeof lastTerminalMessage.agentSessionCursor === "number") {
|
|
299
|
+
return lastTerminalMessage.agentSessionCursor;
|
|
300
|
+
}
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
297
303
|
export function mapTelaAgentSessionToMessageUpdate(session, options = {}) {
|
|
298
304
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
299
|
-
const
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
305
|
+
const reasoningData = session.steps.map((step) => normalizeTelaAgentReasoningStep(session.sessionId, step)).filter((step) => Boolean(step));
|
|
306
|
+
const accumulatedReasoningData = [...options.existingReasoningData ?? []];
|
|
307
|
+
const reasoningIndexes = new Map(accumulatedReasoningData.map((item, index) => [item.uuid, index]));
|
|
308
|
+
const changedReasoningData = [];
|
|
309
|
+
for (const item of reasoningData) {
|
|
310
|
+
const existingIndex = reasoningIndexes.get(item.uuid);
|
|
311
|
+
if (existingIndex === void 0) {
|
|
312
|
+
reasoningIndexes.set(item.uuid, accumulatedReasoningData.length);
|
|
313
|
+
accumulatedReasoningData.push(item);
|
|
314
|
+
changedReasoningData.push(item);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const existingItem = accumulatedReasoningData[existingIndex];
|
|
318
|
+
const existingContent = JSON.stringify({
|
|
319
|
+
type: existingItem.type,
|
|
320
|
+
message: existingItem.message,
|
|
321
|
+
parent_tool_use_id: existingItem.parent_tool_use_id
|
|
322
|
+
});
|
|
323
|
+
const nextContent = JSON.stringify({
|
|
324
|
+
type: item.type,
|
|
325
|
+
message: item.message,
|
|
326
|
+
parent_tool_use_id: item.parent_tool_use_id
|
|
327
|
+
});
|
|
328
|
+
if (existingContent !== nextContent) {
|
|
329
|
+
accumulatedReasoningData[existingIndex] = item;
|
|
330
|
+
changedReasoningData.push(item);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const lastChangedReasoning = changedReasoningData.at(-1);
|
|
306
334
|
const update = {};
|
|
307
|
-
|
|
335
|
+
const nextCursor = typeof session.nextCursor === "number" ? session.nextCursor : void 0;
|
|
336
|
+
if (nextCursor !== void 0) {
|
|
337
|
+
update.agentSessionCursor = nextCursor;
|
|
338
|
+
update.agentSessionId = session.sessionId;
|
|
339
|
+
}
|
|
340
|
+
if (changedReasoningData.length > 0)
|
|
308
341
|
update.reasoningData = accumulatedReasoningData;
|
|
309
|
-
if (
|
|
310
|
-
update.externalUuid =
|
|
311
|
-
if (!options.pendingMessageTtft && options.userMessageCreatedAt &&
|
|
342
|
+
if (lastChangedReasoning)
|
|
343
|
+
update.externalUuid = lastChangedReasoning.uuid;
|
|
344
|
+
if (!options.pendingMessageTtft && options.userMessageCreatedAt && changedReasoningData.length > 0) {
|
|
312
345
|
update.ttft = String(now.getTime() - options.userMessageCreatedAt.getTime());
|
|
313
346
|
}
|
|
314
347
|
if (session.status === "completed" || session.status === "waiting_messages") {
|
|
@@ -323,6 +356,8 @@ export function mapTelaAgentSessionToMessageUpdate(session, options = {}) {
|
|
|
323
356
|
update.content = INTERRUPTED_GENERATION_TEXT;
|
|
324
357
|
update.status = "completed";
|
|
325
358
|
update.updatedAt = now;
|
|
359
|
+
} else if (changedReasoningData.length > 0 || nextCursor !== void 0) {
|
|
360
|
+
update.updatedAt = now;
|
|
326
361
|
}
|
|
327
362
|
return update;
|
|
328
363
|
}
|