@meistrari/chat-nuxt 1.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "1.12.0",
4
+ "version": "1.13.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -19,7 +19,7 @@ const props = defineProps({
19
19
  isPreviewMode: { type: Boolean, required: false },
20
20
  completed: { type: Boolean, required: false }
21
21
  });
22
- const { conjugateText } = useVerbConjugation();
22
+ const { conjugateText, resolveReadFileStepTitle } = useVerbConjugation();
23
23
  const { settings: workspaceSettings } = useWorkspaceSettings();
24
24
  const canvasToolNames = computed(() => {
25
25
  const names = /* @__PURE__ */ new Set();
@@ -193,6 +193,9 @@ const stepTitle = computed(() => {
193
193
  const verb = stepWasExecuted.value ? "Leu" : "Lendo";
194
194
  if (typeof filePath === "string") {
195
195
  const fileName = filePath.split("/").pop() || "arquivo";
196
+ const readTitle = resolveReadFileStepTitle(fileName, stepWasExecuted.value);
197
+ if (readTitle)
198
+ return readTitle;
196
199
  title = `${verb}: ${fileName.length > 40 ? `${fileName.slice(0, 40)}...` : fileName}`;
197
200
  } else {
198
201
  title = `${verb} arquivo`;
@@ -3,4 +3,5 @@ export declare function useVerbConjugation(): {
3
3
  isEnglish: (text: string) => boolean;
4
4
  translateEnglishText: (text: string, toPast: boolean) => string;
5
5
  conjugatePortugueseVerb: (text: string) => string;
6
+ resolveReadFileStepTitle: (fileName: string, wasExecuted: boolean) => string | null;
6
7
  };
@@ -186,6 +186,12 @@ const gerundToPast = new Map(
186
186
  const infinitiveToPast = new Map(
187
187
  ptValues.map((v) => [v.infinitive, v.past])
188
188
  );
189
+ const readFileStepLabels = {
190
+ "instructions.md": "instru\xE7\xF5es",
191
+ "output-format.json": "estrutura de sa\xEDda esperada",
192
+ "input-list.json": "lista de arquivos de entrada",
193
+ "result.json": "resultado"
194
+ };
189
195
  export function useVerbConjugation() {
190
196
  function isEnglish(text) {
191
197
  const firstWord = text.trim().split(/\s+/)[0]?.toLowerCase() || "";
@@ -247,10 +253,17 @@ export function useVerbConjugation() {
247
253
  }
248
254
  return conjugatePortugueseVerb(text);
249
255
  }
256
+ function resolveReadFileStepTitle(fileName, wasExecuted) {
257
+ const label = readFileStepLabels[fileName];
258
+ if (!label)
259
+ return null;
260
+ return conjugateText(`Lendo ${label}`, wasExecuted);
261
+ }
250
262
  return {
251
263
  conjugateText,
252
264
  isEnglish,
253
265
  translateEnglishText,
254
- conjugatePortugueseVerb
266
+ conjugatePortugueseVerb,
267
+ resolveReadFileStepTitle
255
268
  };
256
269
  }
@@ -64,6 +64,8 @@ export default defineEventHandler(async (event) => {
64
64
  reasoningData: null,
65
65
  files: null,
66
66
  ttft: null,
67
+ agentSessionCursor: null,
68
+ agentSessionId: null,
67
69
  updatedAt: null
68
70
  }).where(eq(schema.messages.id, messageId));
69
71
  logger.info({ conversationId, messageId }, "Message retry initiated");
@@ -11,6 +11,7 @@ import {
11
11
  hasPendingMessageExceededGraceWindow,
12
12
  isTerminalMessageUpdate,
13
13
  mapAgentApiSessionToMessageUpdate,
14
+ PENDING_RESPONSE_GRACE_MS,
14
15
  SESSION_STATUS_CHECK_ERROR,
15
16
  THREAD_SESSION_START_ERROR,
16
17
  trackAgentApiConversationUsage,
@@ -18,8 +19,9 @@ import {
18
19
  } from "#chat-runtime/server/utils/conversation-message-sync";
19
20
  import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
20
21
  import { getTelaAgentSession } from "#chat-runtime/server/utils/tela-agent-api";
21
- import { mapTelaAgentSessionToMessageUpdate, mapTelaAgentUsage } from "#chat-runtime/server/utils/tela-agent-session";
22
+ import { mapTelaAgentSessionToMessageUpdate, mapTelaAgentUsage, resolveTelaAgentRequestCursor } from "#chat-runtime/server/utils/tela-agent-session";
22
23
  import { logger } from "#chat-runtime/server/utils/logger";
24
+ const TELA_AGENT_PROGRESS_RECOVERY_MS = 5 * 6e4;
23
25
  export default defineEventHandler(async (event) => {
24
26
  const context = resolveChatRuntimeContext(event);
25
27
  const token = getAgentDataToken(event);
@@ -70,18 +72,38 @@ export default defineEventHandler(async (event) => {
70
72
  }
71
73
  logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId }, "Polling pending message");
72
74
  if (conversation.telaAgentId) {
73
- const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId);
75
+ const agentSessionCursor = resolveTelaAgentRequestCursor({
76
+ pendingMessage,
77
+ lastTerminalMessage: lastTerminalAssistantMessage,
78
+ threadSessionId: conversation.threadSessionId
79
+ });
80
+ const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId, {
81
+ cursor: agentSessionCursor
82
+ });
74
83
  if (!sessionResult2.success) {
75
- return { messages };
84
+ const lastProgressAt = pendingMessage.updatedAt ?? pendingMessage.createdAt;
85
+ const hasTelaAgentProgress = typeof pendingMessage.agentSessionCursor === "number" || !!pendingMessage.externalUuid || Array.isArray(pendingMessage.reasoningData) && pendingMessage.reasoningData.length > 0;
86
+ const recoveryMs = hasTelaAgentProgress ? TELA_AGENT_PROGRESS_RECOVERY_MS : PENDING_RESPONSE_GRACE_MS;
87
+ if (!hasPendingMessageExceededGraceWindow({ createdAt: lastProgressAt }, /* @__PURE__ */ new Date(), recoveryMs)) {
88
+ logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId, error: sessionResult2.error }, "Tela agent session status check failed within grace window");
89
+ return { messages };
90
+ }
91
+ const updateData3 = buildFailedPendingMessageUpdate(SESSION_STATUS_CHECK_ERROR);
92
+ logger.warn({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId, error: sessionResult2.error }, "Pending message failed because Tela agent session status check did not recover");
93
+ await db.update(schema.messages).set(updateData3).where(eq(schema.messages.id, pendingMessage.id));
94
+ return buildMessageUpdateResponse(messages, pendingMessage.id, updateData3);
76
95
  }
77
96
  const agentSession2 = sessionResult2.data;
78
97
  const userMessage2 = messages.findLast((message) => message.role === "user");
79
98
  const updateData2 = mapTelaAgentSessionToMessageUpdate(agentSession2, {
80
- lastSyncedStepId: pendingMessage.externalUuid ?? lastTerminalAssistantMessage?.externalUuid,
81
99
  existingReasoningData: Array.isArray(pendingMessage.reasoningData) ? pendingMessage.reasoningData : null,
82
100
  pendingMessageTtft: pendingMessage.ttft,
83
101
  userMessageCreatedAt: userMessage2?.createdAt ?? null
84
102
  });
103
+ if (typeof agentSessionCursor === "number" && pendingMessage.agentSessionCursor !== agentSessionCursor && updateData2.agentSessionCursor === void 0) {
104
+ updateData2.agentSessionCursor = agentSessionCursor;
105
+ updateData2.agentSessionId = conversation.threadSessionId;
106
+ }
85
107
  await attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData2, token);
86
108
  if (!hasMessageUpdate(updateData2)) {
87
109
  return { messages };
@@ -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(),
@@ -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;