@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.
Files changed (89) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +15 -0
  3. package/dist/runtime/components/chat/conversation-item.d.vue.ts +6 -1
  4. package/dist/runtime/components/chat/conversation-item.vue +30 -141
  5. package/dist/runtime/components/chat/conversation-item.vue.d.ts +6 -1
  6. package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
  7. package/dist/runtime/components/chat/conversation-list.vue +300 -72
  8. package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
  9. package/dist/runtime/components/chat/conversation-new-group-button.d.vue.ts +7 -0
  10. package/dist/runtime/components/chat/conversation-new-group-button.vue +90 -0
  11. package/dist/runtime/components/chat/conversation-new-group-button.vue.d.ts +7 -0
  12. package/dist/runtime/components/chat/conversation-new-group-popover-content.d.vue.ts +23 -0
  13. package/dist/runtime/components/chat/conversation-new-group-popover-content.vue +75 -0
  14. package/dist/runtime/components/chat/conversation-new-group-popover-content.vue.d.ts +23 -0
  15. package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +2 -2
  16. package/dist/runtime/components/chat/mobile/shell/drawer.vue +370 -41
  17. package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +2 -2
  18. package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +6 -6
  19. package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
  20. package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +6 -6
  21. package/dist/runtime/components/chat/reasoning-message.vue +4 -1
  22. package/dist/runtime/components/chat/topbar.d.vue.ts +2 -2
  23. package/dist/runtime/components/chat/topbar.vue.d.ts +2 -2
  24. package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
  25. package/dist/runtime/composables/useConversationGroups.js +160 -0
  26. package/dist/runtime/composables/useConversationItem.d.ts +48 -0
  27. package/dist/runtime/composables/useConversationItem.js +176 -0
  28. package/dist/runtime/composables/useConversationList.d.ts +128 -0
  29. package/dist/runtime/composables/useConversationList.js +553 -0
  30. package/dist/runtime/composables/useConversations.d.ts +2 -0
  31. package/dist/runtime/composables/useConversations.js +45 -1
  32. package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
  33. package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
  34. package/dist/runtime/composables/useVerbConjugation.d.ts +1 -0
  35. package/dist/runtime/composables/useVerbConjugation.js +14 -1
  36. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
  37. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
  38. package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
  39. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
  40. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
  41. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
  42. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
  43. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
  44. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
  45. package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
  46. package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
  47. package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
  48. package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
  49. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
  50. package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
  51. package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
  52. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +2 -0
  53. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +26 -4
  54. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
  55. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
  56. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
  57. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
  58. package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
  59. package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
  60. package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
  61. package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
  62. package/dist/runtime/server/api/conversations/index.post.js +1 -0
  63. package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
  64. package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
  65. package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
  66. package/dist/runtime/server/db/schema/conversations.js +3 -0
  67. package/dist/runtime/server/db/schema/index.d.ts +1 -0
  68. package/dist/runtime/server/db/schema/index.js +1 -0
  69. package/dist/runtime/server/db/schema/messages.d.ts +34 -0
  70. package/dist/runtime/server/db/schema/messages.js +3 -1
  71. package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
  72. package/dist/runtime/server/utils/conversation-group.js +48 -0
  73. package/dist/runtime/server/utils/conversation-message-sync.d.ts +1 -1
  74. package/dist/runtime/server/utils/conversation-message-sync.js +2 -2
  75. package/dist/runtime/server/utils/tela-agent-api.d.ts +3 -1
  76. package/dist/runtime/server/utils/tela-agent-api.js +285 -26
  77. package/dist/runtime/server/utils/tela-agent-session.d.ts +14 -4
  78. package/dist/runtime/server/utils/tela-agent-session.js +57 -22
  79. package/dist/runtime/types/chat.d.ts +2 -1
  80. package/dist/runtime/types/tela-agent.d.ts +155 -43
  81. package/dist/runtime/types/tela-agent.js +49 -11
  82. package/dist/runtime/utils/conversation-groups.d.ts +18 -0
  83. package/dist/runtime/utils/conversation-groups.js +30 -0
  84. package/drizzle/0016_dry_aaron_stack.sql +16 -0
  85. package/drizzle/0017_typical_dexter_bennett.sql +2 -0
  86. package/drizzle/meta/0016_snapshot.json +887 -0
  87. package/drizzle/meta/0017_snapshot.json +899 -0
  88. package/drizzle/meta/_journal.json +14 -0
  89. 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() >= PENDING_RESPONSE_GRACE_MS;
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): Promise<{
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
- telaAgentSessionApiResponseSchema
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
- async function telaAgentFetch(event, path, init) {
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 response = await fetch(url, {
35
- ...init,
36
- headers: {
37
- "Content-Type": "application/json",
38
- "x-data-token": token,
39
- "Authorization": `Bearer ${token}`,
40
- ...init.headers
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
- const body = await readResponseBody(response);
44
- if (!response.ok) {
45
- const error = getTelaErrorMessage(body, `Tela API error: ${response.status} - ${response.statusText}`);
46
- logger.warn({ status: response.status, statusText: response.statusText, path }, "Tela agent API request failed");
47
- return { success: false, error };
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 result = await telaAgentFetch(event, `/agent/sessions/${encodedSessionId}`, {
83
- method: "GET"
84
- });
85
- if (!result.success)
86
- return result;
87
- const parsed = telaAgentSessionApiResponseSchema.safeParse(result.body);
88
- if (!parsed.success) {
89
- logger.warn({ issues: parsed.error.issues, sessionId }, "Tela agent session response parse error");
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 getNewTelaAgentSteps(session: TelaAgentSession, lastSyncedStepId: string | null | undefined): TelaAgentStep[];
39
- export declare function extractTelaAgentAssistantContent(session: TelaAgentSession, steps?: TelaAgentStep[]): string;
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: getTelaAgentStepSyncId(step),
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 getNewTelaAgentSteps(session, lastSyncedStepId) {
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 newSteps = getNewTelaAgentSteps(session, options.lastSyncedStepId);
300
- const reasoningData = newSteps.map((step) => normalizeTelaAgentReasoningStep(session.sessionId, step)).filter((step) => Boolean(step));
301
- const accumulatedReasoningData = [
302
- ...options.existingReasoningData ?? [],
303
- ...reasoningData
304
- ];
305
- const lastStep = newSteps.at(-1);
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
- if (reasoningData.length > 0)
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 (reasoningData.length > 0 && lastStep)
310
- update.externalUuid = getTelaAgentStepSyncId(lastStep);
311
- if (!options.pendingMessageTtft && options.userMessageCreatedAt && reasoningData.length > 0) {
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
  }