n8n-nodes-bgos 1.3.4 → 1.4.1

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.
@@ -11,8 +11,9 @@ class BGOSAction {
11
11
  group: ['transform'],
12
12
  version: 1,
13
13
  subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
14
- description: 'Interact with the BGOS platform',
14
+ description: 'Interact with the BGOS chat platform. Send/edit/pin/delete messages, ask the user a question, send a message and wait for the reply, fetch chat metadata, manage assistants and files. Usable as an AI agent tool.',
15
15
  defaults: { name: 'BGOS Action' },
16
+ usableAsTool: true,
16
17
  inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
17
18
  outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
18
19
  credentials: [
@@ -45,22 +46,40 @@ class BGOSAction {
45
46
  noDataExpression: true,
46
47
  displayOptions: { show: { resource: ['message'] } },
47
48
  options: [
48
- {
49
- name: 'Delete a Message',
50
- value: 'deleteMessage',
51
- description: 'Delete a message from a chat',
52
- action: 'Delete a message',
53
- },
54
49
  {
55
50
  name: 'Send a Message',
56
51
  value: 'sendMessage',
57
- description: 'Send an assistant reply to the user with optional file attachment',
52
+ description: 'Post a new message to a chat. Use for proactive notifications, assistant replies, status updates, or anything you want to say to the user or another agent. Supports text, file attachments, and tappable inline/modal buttons.',
58
53
  action: 'Send a message',
59
54
  },
55
+ {
56
+ name: 'Send and Wait for Reply',
57
+ value: 'sendAndWaitForReply',
58
+ description: 'Post a message and BLOCK until the next reply arrives in the chat (default 10 min timeout). Use this when an AI agent calls another agent (or the user) and needs the response back as the tool result. Returns { sentMessageId, replyMessageId, replyText }.',
59
+ action: 'Send a message and wait for reply',
60
+ },
61
+ {
62
+ name: 'Edit a Message',
63
+ value: 'editMessage',
64
+ description: 'Replace the text of a previously-sent message in place. Use to update a status message ("Working..." → "Done") instead of spamming new messages. Requires Message ID and User ID.',
65
+ action: 'Edit a message',
66
+ },
67
+ {
68
+ name: 'Pin or Unpin a Message',
69
+ value: 'pinMessage',
70
+ description: 'Toggle the pinned state of a message. Pinned messages appear in the chat\'s pin bar. Set "Is Pinned" to true to pin or false to unpin.',
71
+ action: 'Pin or unpin a message',
72
+ },
73
+ {
74
+ name: 'Delete a Message',
75
+ value: 'deleteMessage',
76
+ description: 'Permanently remove a message from a chat. Use sparingly — most workflows should edit the text rather than delete + resend.',
77
+ action: 'Delete a message',
78
+ },
60
79
  {
61
80
  name: 'Ask User Input',
62
81
  value: 'askUserInput',
63
- description: 'Ask the user 1–4 multiple-choice questions through the polished modal/sheet in the BGOS app',
82
+ description: 'Ask the user 1–4 multiple-choice questions through a polished modal/sheet (or inline carousel). BLOCKS until answered. Use ONLY when the user is actively in conversation and you need their immediate choice; for async/scheduled prompts use Send a Message with buttons instead.',
64
83
  action: 'Ask user input',
65
84
  },
66
85
  ],
@@ -76,25 +95,31 @@ class BGOSAction {
76
95
  displayOptions: { show: { resource: ['chat'] } },
77
96
  options: [
78
97
  {
79
- name: 'Delete a Chat',
80
- value: 'deleteChat',
81
- description: 'Delete a chat',
82
- action: 'Delete a chat',
98
+ name: 'Get a Chat',
99
+ value: 'getChat',
100
+ description: 'Fetch chat metadata (title, assistant, ownership, timestamps) by chat ID. Use to gather context before deciding how to respond — e.g. checking the assigned assistant or the chat title.',
101
+ action: 'Get a chat',
102
+ },
103
+ {
104
+ name: 'Set Title on a Chat',
105
+ value: 'renameChat',
106
+ description: 'Rename a chat. Use after a meaningful event (e.g. summarising the topic of a conversation, or marking a chat as resolved).',
107
+ action: 'Set title on a chat',
83
108
  },
84
109
  {
85
110
  name: 'Save Chat History',
86
111
  value: 'saveChatHistory',
87
- description: 'Bulk-save messages to a chat',
112
+ description: 'Bulk-insert a list of messages into a chat. Use for migrations or for replaying conversation history; not for normal sending — use "Send a Message" for that.',
88
113
  action: 'Save chat history',
89
114
  },
90
115
  {
91
- name: 'Set Title on a Chat',
92
- value: 'renameChat',
93
- description: 'Rename a chat',
94
- action: 'Set title on a chat',
116
+ name: 'Delete a Chat',
117
+ value: 'deleteChat',
118
+ description: 'Permanently delete a chat and its messages. Destructive — use only for explicit cleanup tasks.',
119
+ action: 'Delete a chat',
95
120
  },
96
121
  ],
97
- default: 'renameChat',
122
+ default: 'getChat',
98
123
  required: true,
99
124
  },
100
125
  // ── Assistant operations ──────────────────────────────────────────────
@@ -251,6 +276,133 @@ class BGOSAction {
251
276
  description: 'ID of the message to delete. Falls back to message_id / messageId from input data.',
252
277
  displayOptions: { show: { resource: ['message'], operation: ['deleteMessage'] } },
253
278
  },
279
+ // ── Edit a Message ────────────────────────────────────────────────────
280
+ {
281
+ displayName: 'Message ID',
282
+ name: 'messageId',
283
+ type: 'string',
284
+ default: '',
285
+ placeholder: '456',
286
+ description: 'ID of the message to edit. Falls back to message_id / messageId from input data.',
287
+ displayOptions: { show: { resource: ['message'], operation: ['editMessage'] } },
288
+ },
289
+ {
290
+ displayName: 'New Text',
291
+ name: 'messageText',
292
+ type: 'string',
293
+ typeOptions: { rows: 4 },
294
+ default: '',
295
+ placeholder: 'Done — here\'s the result:',
296
+ description: 'The new message text. Replaces the existing text entirely. Falls back to the input data\'s text field.',
297
+ displayOptions: { show: { resource: ['message'], operation: ['editMessage'] } },
298
+ },
299
+ {
300
+ displayName: 'User ID',
301
+ name: 'userId',
302
+ type: 'string',
303
+ default: '',
304
+ placeholder: 'user_xxx',
305
+ description: 'User ID of the message owner (for ownership validation). Falls back to user_id from input data.',
306
+ displayOptions: { show: { resource: ['message'], operation: ['editMessage'] } },
307
+ },
308
+ // ── Pin or Unpin a Message ────────────────────────────────────────────
309
+ {
310
+ displayName: 'Message ID',
311
+ name: 'messageId',
312
+ type: 'string',
313
+ default: '',
314
+ placeholder: '456',
315
+ description: 'ID of the message to pin or unpin. Falls back to message_id / messageId from input data.',
316
+ displayOptions: { show: { resource: ['message'], operation: ['pinMessage'] } },
317
+ },
318
+ {
319
+ displayName: 'Is Pinned',
320
+ name: 'isPinned',
321
+ type: 'boolean',
322
+ default: true,
323
+ description: 'Whether the message should be pinned (true) or unpinned (false). Pinned messages appear in the chat\'s pin bar.',
324
+ displayOptions: { show: { resource: ['message'], operation: ['pinMessage'] } },
325
+ },
326
+ // ── Send and Wait for Reply ───────────────────────────────────────────
327
+ {
328
+ displayName: 'Assistant ID',
329
+ name: 'assistantId',
330
+ type: 'string',
331
+ default: '',
332
+ placeholder: '42',
333
+ description: 'ID of the assistant sending the message. Falls back to assistantId / assistant_id from input data.',
334
+ displayOptions: { show: { resource: ['message'], operation: ['sendAndWaitForReply'] } },
335
+ },
336
+ {
337
+ displayName: 'Chat ID',
338
+ name: 'chatId',
339
+ type: 'string',
340
+ default: '',
341
+ placeholder: '123',
342
+ description: 'ID of the chat to post into and listen on. Falls back to chatId / chat_id from input data.',
343
+ displayOptions: { show: { resource: ['message'], operation: ['sendAndWaitForReply'] } },
344
+ },
345
+ {
346
+ displayName: 'Text',
347
+ name: 'messageText',
348
+ type: 'string',
349
+ typeOptions: { rows: 4 },
350
+ default: '',
351
+ placeholder: 'Hey Ava, can you check on the latest sales numbers?',
352
+ description: 'Message text to post. The next reply in the chat will be returned as the tool result.',
353
+ displayOptions: { show: { resource: ['message'], operation: ['sendAndWaitForReply'] } },
354
+ },
355
+ {
356
+ displayName: 'Additional Fields',
357
+ name: 'replyAdditionalFields',
358
+ type: 'collection',
359
+ placeholder: 'Add Field',
360
+ default: {},
361
+ displayOptions: { show: { resource: ['message'], operation: ['sendAndWaitForReply'] } },
362
+ options: [
363
+ {
364
+ displayName: 'Sender',
365
+ name: 'sender',
366
+ type: 'options',
367
+ default: 'assistant',
368
+ options: [
369
+ { name: 'Assistant', value: 'assistant' },
370
+ { name: 'User', value: 'user' },
371
+ ],
372
+ description: 'Who is sending the outgoing message. Defaults to Assistant.',
373
+ },
374
+ {
375
+ displayName: 'File ID',
376
+ name: 'fileId',
377
+ type: 'string',
378
+ default: '',
379
+ placeholder: 'abc-123',
380
+ description: 'ID of a previously uploaded file to attach to the outgoing message. Use the "Upload a File" operation first to get the file ID.',
381
+ },
382
+ {
383
+ displayName: 'User ID',
384
+ name: 'userId',
385
+ type: 'string',
386
+ default: '',
387
+ placeholder: 'user_xxx',
388
+ description: 'User ID that owns the chat (required by the poll endpoint). Auto-falls-back to user_id / userId on the input data — typically passed in by an upstream BGOS Trigger — so most workflows can leave this empty.',
389
+ },
390
+ {
391
+ displayName: 'Timeout (Seconds)',
392
+ name: 'replyTimeoutSeconds',
393
+ type: 'number',
394
+ default: 600,
395
+ description: 'How long to wait for a reply before failing the node (default 10 minutes).',
396
+ },
397
+ {
398
+ displayName: 'Poll Interval (Ms)',
399
+ name: 'replyPollIntervalMs',
400
+ type: 'number',
401
+ default: 2000,
402
+ description: 'How often to check for new messages while waiting (default 2 seconds; minimum 1000).',
403
+ },
404
+ ],
405
+ },
254
406
  // ── Ask User Input ────────────────────────────────────────────────────
255
407
  {
256
408
  displayName: 'Chat ID',
@@ -395,7 +547,7 @@ class BGOSAction {
395
547
  default: '',
396
548
  placeholder: '123',
397
549
  description: 'ID of the chat. Falls back to chat_id / chat.id from input data.',
398
- displayOptions: { show: { resource: ['chat'], operation: ['renameChat', 'deleteChat', 'saveChatHistory'] } },
550
+ displayOptions: { show: { resource: ['chat'], operation: ['renameChat', 'deleteChat', 'saveChatHistory', 'getChat'] } },
399
551
  },
400
552
  {
401
553
  displayName: 'New Title',
@@ -557,7 +709,7 @@ class BGOSAction {
557
709
  type: 'string',
558
710
  default: '',
559
711
  placeholder: 'user_xxx',
560
- description: 'Override the user ID. If empty, falls back to user_id from the input data.',
712
+ description: 'Override the user ID. If empty, falls back to user_id from the input data. Optional for "Get a Chat".',
561
713
  displayOptions: {
562
714
  show: {
563
715
  resource: ['chat', 'assistant', 'callback', 'file'],
@@ -599,6 +751,30 @@ class BGOSAction {
599
751
  if (operation === 'deleteMessage') {
600
752
  nodeParams.deleteMessageId = String(this.getNodeParameter('deleteMessageId', i, '') ?? '');
601
753
  }
754
+ if (operation === 'editMessage') {
755
+ nodeParams.messageId = String(this.getNodeParameter('messageId', i, '') ?? '');
756
+ nodeParams.messageText = String(this.getNodeParameter('messageText', i, '') ?? '');
757
+ nodeParams.userId = String(this.getNodeParameter('userId', i, '') ?? '');
758
+ }
759
+ if (operation === 'pinMessage') {
760
+ nodeParams.messageId = String(this.getNodeParameter('messageId', i, '') ?? '');
761
+ nodeParams.isPinned = Boolean(this.getNodeParameter('isPinned', i, true));
762
+ }
763
+ if (operation === 'sendAndWaitForReply') {
764
+ nodeParams.assistantId = String(this.getNodeParameter('assistantId', i, '') ?? '');
765
+ nodeParams.chatId = String(this.getNodeParameter('chatId', i, '') ?? '');
766
+ nodeParams.messageText = String(this.getNodeParameter('messageText', i, '') ?? '');
767
+ const replyAdditional = this.getNodeParameter('replyAdditionalFields', i, {});
768
+ nodeParams.userId = String(replyAdditional.userId ?? '');
769
+ nodeParams.sender = String(replyAdditional.sender ?? 'assistant');
770
+ nodeParams.fileId = String(replyAdditional.fileId ?? '');
771
+ nodeParams.replyTimeoutSeconds = Number(replyAdditional.replyTimeoutSeconds ?? 600);
772
+ nodeParams.replyPollIntervalMs = Number(replyAdditional.replyPollIntervalMs ?? 2000);
773
+ }
774
+ if (operation === 'getChat') {
775
+ nodeParams.chatId = String(this.getNodeParameter('chatId', i, '') ?? '');
776
+ nodeParams.userId = String(this.getNodeParameter('userId', i, '') ?? '');
777
+ }
602
778
  if (operation === 'askUserInput') {
603
779
  nodeParams.chatId = String(this.getNodeParameter('chatId', i, '') ?? '');
604
780
  nodeParams.userId = String(this.getNodeParameter('askUserId', i, '') ?? '');
@@ -38,6 +38,9 @@ type NodeParams = {
38
38
  askTimeoutSeconds?: number;
39
39
  askPollIntervalMs?: number;
40
40
  renderMode?: string;
41
+ isPinned?: boolean;
42
+ replyTimeoutSeconds?: number;
43
+ replyPollIntervalMs?: number;
41
44
  [key: string]: unknown;
42
45
  };
43
46
  export declare function handleEventByType(this: IExecuteFunctions, eventType: string, eventData: Record<string, unknown>, nodeParams: NodeParams): Promise<unknown>;
@@ -81,6 +81,86 @@ async function handleEventByType(eventType, eventData, nodeParams) {
81
81
  }
82
82
  return await techWebhook_1.deleteMessage.call(this, apiOptions, String(messageId));
83
83
  }
84
+ case 'editMessage': {
85
+ const apiOptions = getApiOptions(nodeParams);
86
+ const messageId = nodeParams.messageId || (eventData.message_id ?? eventData.messageId ?? message?.id);
87
+ const userId = (nodeParams.userId ?? eventData.user_id ?? eventData.userId);
88
+ const newText = (nodeParams.messageText ?? '').trim() || (eventData.text ?? message?.text ?? '');
89
+ if (!messageId || !userId || !newText) {
90
+ throw new Error('Message ID, User ID, and Text are required for Edit a Message. Set the fields or make sure input data contains message_id, user_id, and text.');
91
+ }
92
+ return await techWebhook_1.editMessageText.call(this, apiOptions, userId, String(messageId), String(newText));
93
+ }
94
+ case 'pinMessage': {
95
+ const apiOptions = getApiOptions(nodeParams);
96
+ const messageId = nodeParams.messageId || (eventData.message_id ?? eventData.messageId ?? message?.id);
97
+ if (!messageId) {
98
+ throw new Error('Message ID is required for Pin/Unpin a Message. Set the field or make sure input data contains message_id.');
99
+ }
100
+ // pinState may come from the node param or input data; default to true (pin)
101
+ const pinStateRaw = nodeParams.isPinned ?? eventData.isPinned ?? eventData.is_pinned ?? true;
102
+ const isPinned = pinStateRaw === true || pinStateRaw === 'true';
103
+ return await techWebhook_1.setMessagePinned.call(this, apiOptions, String(messageId), isPinned);
104
+ }
105
+ case 'sendAndWaitForReply': {
106
+ const apiOptions = getApiOptions(nodeParams);
107
+ const assistantId = nodeParams.assistantId || (eventData.assistantId ?? eventData.assistant_id ?? assistant?.id);
108
+ const chatId = nodeParams.chatId || (eventData.chatId ?? eventData.chat_id ?? chat?.id ?? message?.chatId);
109
+ const userId = ((nodeParams.userId && String(nodeParams.userId).trim()) || eventData.user_id || eventData.userId);
110
+ const text = (nodeParams.messageText ?? '').trim() || (eventData.text ?? message?.text ?? '');
111
+ if (!assistantId || !chatId || !userId || !text) {
112
+ throw new Error('Assistant ID, Chat ID, Text, and a User ID (from input data or the Additional Fields override) are required for Send and Wait for Reply. Make sure the upstream BGOS Trigger output includes user_id.');
113
+ }
114
+ // Optional file attachment
115
+ const fileIdParam = nodeParams.fileId?.trim();
116
+ const files = fileIdParam ? [{ id: fileIdParam }] : [];
117
+ const sender = nodeParams.sender || 'assistant';
118
+ // Step 1: post the outgoing message and capture its id
119
+ const sendResult = (await techWebhook_1.sendMessageToBackend.call(this, apiOptions, {
120
+ assistantId: assistantId,
121
+ chatId: chatId,
122
+ sender,
123
+ text,
124
+ files,
125
+ isMixedAttachments: files.length > 0 ? true : null,
126
+ }));
127
+ const sentMessageId = Number(sendResult?.id
128
+ ?? sendResult?.message?.id
129
+ ?? 0);
130
+ if (!sentMessageId) {
131
+ throw new Error('Could not determine sent message ID from BGOS response — cannot wait for reply.');
132
+ }
133
+ // Step 2: poll the chat for any new message with id > sentMessageId
134
+ const timeoutSeconds = Number(nodeParams.replyTimeoutSeconds ?? 600);
135
+ const pollIntervalMs = Math.max(1000, Number(nodeParams.replyPollIntervalMs ?? 2000));
136
+ const deadline = Date.now() + timeoutSeconds * 1000;
137
+ while (Date.now() < deadline) {
138
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
139
+ try {
140
+ const data = await techWebhook_1.getChatMessages.call(this, apiOptions, Number(chatId), userId);
141
+ // Cast to a permissive shape — backend returns extra fields beyond ChatMessageEntry
142
+ const msgs = (data?.messages ?? []);
143
+ for (const entry of msgs) {
144
+ const m = (entry.message ?? entry);
145
+ const id = Number(m.id ?? 0);
146
+ const replyText = m.text;
147
+ if (id > sentMessageId && replyText) {
148
+ return {
149
+ success: true,
150
+ sentMessageId,
151
+ replyMessageId: id,
152
+ replyText,
153
+ raw: entry,
154
+ };
155
+ }
156
+ }
157
+ }
158
+ catch {
159
+ // Network blip — keep polling.
160
+ }
161
+ }
162
+ throw new Error(`Timed out after ${timeoutSeconds}s waiting for a reply to message ${sentMessageId} in chat ${chatId}.`);
163
+ }
84
164
  case 'askUserInput': {
85
165
  const apiOptions = getApiOptions(nodeParams);
86
166
  const chatId = nodeParams.chatId || (eventData.chatId ?? eventData.chat_id ?? chat?.id ?? message?.chatId);
@@ -243,6 +323,14 @@ async function handleEventByType(eventType, eventData, nodeParams) {
243
323
  throw new Error('Chat ID is required for Delete a Chat.');
244
324
  return await techWebhook_1.deleteChat.call(this, getApiOptions(nodeParams), userId, String(chatId));
245
325
  }
326
+ case 'getChat': {
327
+ const apiOptions = getApiOptions(nodeParams);
328
+ const chatId = nodeParams.chatId || (eventData.chat_id ?? eventData.chatId ?? chat?.id);
329
+ if (!chatId)
330
+ throw new Error('Chat ID is required for Get a Chat.');
331
+ const userId = (nodeParams.userId ?? eventData.user_id ?? eventData.userId);
332
+ return await techWebhook_1.getChat.call(this, apiOptions, String(chatId), userId);
333
+ }
246
334
  case 'saveChatHistory': {
247
335
  const userId = (nodeParams.userId ?? eventData.user_id ?? eventData.userId);
248
336
  if (!userId)
@@ -18,6 +18,24 @@ export declare function saveChatHistory(this: IExecuteFunctions, options: BgosAp
18
18
  export declare function deleteMessage(this: IExecuteFunctions, options: BgosApiOptions, messageId: string | number): Promise<{
19
19
  success: boolean;
20
20
  }>;
21
+ /**
22
+ * PATCH /api/v1/messages/:id — update the text of an existing message.
23
+ * Useful for status-updates-in-place (e.g. "Working..." → "Done"), replacing
24
+ * the prior approach of sending a new message every time.
25
+ */
26
+ export declare function editMessageText(this: IExecuteFunctions, options: BgosApiOptions, userId: string, messageId: string | number, text: string): Promise<unknown>;
27
+ /**
28
+ * PATCH /api/v1/messages/:id/pin — toggle the pin state of a message.
29
+ * Body { isPinned: boolean }. Backend stamps pinned_at when true,
30
+ * clears it when false. Emits a message_pinned WebSocket event.
31
+ */
32
+ export declare function setMessagePinned(this: IExecuteFunctions, options: BgosApiOptions, messageId: string | number, isPinned: boolean): Promise<unknown>;
33
+ /**
34
+ * GET /api/v1/chats/:chatId?userId=... — fetch chat metadata (title,
35
+ * assistantId, ownership). Useful for AI agents that need context before
36
+ * deciding how to respond.
37
+ */
38
+ export declare function getChat(this: IExecuteFunctions, options: BgosApiOptions, chatId: string | number, userId?: string): Promise<unknown>;
21
39
  export declare function sendMessageToBackend(this: IExecuteFunctions, options: BgosApiOptions, payload: {
22
40
  assistantId: string | number;
23
41
  chatId: string | number;
@@ -7,6 +7,9 @@ exports.renameChat = renameChat;
7
7
  exports.deleteChat = deleteChat;
8
8
  exports.saveChatHistory = saveChatHistory;
9
9
  exports.deleteMessage = deleteMessage;
10
+ exports.editMessageText = editMessageText;
11
+ exports.setMessagePinned = setMessagePinned;
12
+ exports.getChat = getChat;
10
13
  exports.sendMessageToBackend = sendMessageToBackend;
11
14
  exports.postAskQuestion = postAskQuestion;
12
15
  exports.getChatMessages = getChatMessages;
@@ -97,6 +100,51 @@ async function deleteMessage(options, messageId) {
97
100
  });
98
101
  return { success: true };
99
102
  }
103
+ /**
104
+ * PATCH /api/v1/messages/:id — update the text of an existing message.
105
+ * Useful for status-updates-in-place (e.g. "Working..." → "Done"), replacing
106
+ * the prior approach of sending a new message every time.
107
+ */
108
+ async function editMessageText(options, userId, messageId, text) {
109
+ const url = `${options.baseUrl.replace(/\/$/, '')}/api/v1/messages/${messageId}`;
110
+ return this.helpers.httpRequest({
111
+ method: 'PATCH',
112
+ url,
113
+ headers: buildHeaders(options.apiKey),
114
+ body: { userId, text },
115
+ json: true,
116
+ });
117
+ }
118
+ /**
119
+ * PATCH /api/v1/messages/:id/pin — toggle the pin state of a message.
120
+ * Body { isPinned: boolean }. Backend stamps pinned_at when true,
121
+ * clears it when false. Emits a message_pinned WebSocket event.
122
+ */
123
+ async function setMessagePinned(options, messageId, isPinned) {
124
+ const url = `${options.baseUrl.replace(/\/$/, '')}/api/v1/messages/${messageId}/pin`;
125
+ return this.helpers.httpRequest({
126
+ method: 'PATCH',
127
+ url,
128
+ headers: buildHeaders(options.apiKey),
129
+ body: { isPinned },
130
+ json: true,
131
+ });
132
+ }
133
+ /**
134
+ * GET /api/v1/chats/:chatId?userId=... — fetch chat metadata (title,
135
+ * assistantId, ownership). Useful for AI agents that need context before
136
+ * deciding how to respond.
137
+ */
138
+ async function getChat(options, chatId, userId) {
139
+ const qs = userId ? `?userId=${encodeURIComponent(userId)}` : '';
140
+ const url = `${options.baseUrl.replace(/\/$/, '')}/api/v1/chats/${chatId}${qs}`;
141
+ return this.helpers.httpRequest({
142
+ method: 'GET',
143
+ url,
144
+ headers: buildHeaders(options.apiKey),
145
+ json: true,
146
+ });
147
+ }
100
148
  async function sendMessageToBackend(options, payload) {
101
149
  const url = `${options.baseUrl.replace(/\/$/, '')}/api/v1/send-message`;
102
150
  const body = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-bgos",
3
- "version": "1.3.4",
3
+ "version": "1.4.1",
4
4
  "description": "n8n community nodes for BGOS (Brand Growth OS) - AI assistant chat platform",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",