json-object-editor 0.10.425 → 0.10.431

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/js/joe-ai.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const Ai = {};
4
4
  const self = this;
5
5
  Ai._openChats = {}; // Conversation ID -> element
6
- Ai.default_ai = null; // Default AI assistant ID
6
+ Ai.default_ai = null; // Default AI assistant object
7
7
  // ========== COMPONENTS ==========
8
8
 
9
9
  class JoeAIChatbox extends HTMLElement {
@@ -13,10 +13,15 @@
13
13
 
14
14
  this.attachShadow({ mode: 'open' });
15
15
  this.messages = [];
16
-
16
+ this.UI={header:null,content:null,footer:null};
17
+ this.conversation = null;
18
+ this.conversation_id = null;
19
+ this.currentRunId = null;
20
+ this.thread_id = null;
21
+ this.user_id = null;
17
22
  }
18
23
 
19
- connectedCallback() {
24
+ async connectedCallback() {
20
25
  this.conversation_id = this.getAttribute('conversation_id');
21
26
 
22
27
  this.selected_assistant_id = Ai.default_ai?Ai.default_ai.value:null;
@@ -25,9 +30,102 @@
25
30
  this.renderError("Missing conversation_id");
26
31
  return;
27
32
  }
28
- this.loadConversation();
33
+
34
+ var c = await this.loadConversation();
29
35
  this.getAllAssistants();
36
+ this.setupUI();
37
+ }
38
+
39
+ setupUI() {
40
+ // Set up UI elements and event listeners
41
+ // Inject external CSS
42
+ const styleLink = document.createElement('link');
43
+ styleLink.setAttribute('rel', 'stylesheet');
44
+ styleLink.setAttribute('href', '/JsonObjectEditor/css/joe-ai.css'); // Adjust path as needed
45
+ this.shadowRoot.appendChild(styleLink);
46
+
47
+
48
+ Ai.default_ai
49
+ /*HEADER*/
50
+ Ai.getDefaultAssistant();
51
+ const assistantOptions = _joe.Data.ai_assistant.map(a => {
52
+
53
+ const label = a.name || a.title || 'Assistant';
54
+ const value = a._id;
55
+ const selected = value === this.selected_assistant_id ? 'selected' : '';
56
+
57
+ return `<option value="${value}" ${selected}>${label}</option>`;
58
+ }).join('');
59
+
60
+ const assistantSelect = `
61
+ <label-select-wrapper>
62
+ <label class="assistant-select-label" title="joe ai assistants">${_joe && _joe.SVG.icon.assistant}</label>
63
+ <select id="assistant-select">
64
+ ${assistantOptions}
65
+ </select>
66
+ </label-select-wrapper>
67
+ `;
68
+
69
+ /*CONTENT*/
70
+ const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
71
+
72
+ // Build inner HTML in a wrapper
73
+ const wrapper = document.createElement('chatbox-wrapper');
74
+ wrapper.className = 'chatbox';
75
+ wrapper.innerHTML = `
76
+ <div class="close-btn" title="Close Chatbox" >${_joe.SVG.icon.close}</div>
77
+ <chat-header>
78
+ <chat-title>${this.conversation.name}</chat-title>
79
+ <p>${this.conversation.info||''}</p>
80
+ ${assistantSelect}
81
+ </chat-header>
82
+ <chat-content>${chatMessages}</chat-content>
83
+ <chat-footer>
84
+ <textarea id="chat-input" type="text" placeholder="Type a message..."></textarea>
85
+ <button id="send-button">Send</button>
86
+ </chat-footer>
87
+ `;
88
+
89
+ this.shadowRoot.appendChild(wrapper);
90
+
91
+ ['header','content','header'].map(u=>{
92
+ this.UI[u] = this.shadowRoot.querySelector('chat-'+u);
93
+ })
94
+ this.UI.content.update = this.updateMessages.bind(this);
95
+ setTimeout(() => {
96
+ this.UI.content.scrollTop = this.UI.content.scrollHeight;
97
+ }, 100);
98
+ this.UI.content.scrollTop = this.UI.content.scrollHeight;
99
+
100
+ this.UI.textarea = this.shadowRoot.getElementById('chat-input');
101
+ this.UI.textarea.addEventListener('keydown', (e) => {
102
+ if (e.key === 'Enter' && !e.shiftKey) {
103
+ e.preventDefault(); // Prevent newline if needed
104
+ console.log('Enter pressed!');
105
+ this.sendMessage();
106
+ }
107
+ });
30
108
 
109
+ this.UI.sendButton = this.shadowRoot.getElementById('send-button');
110
+ this.UI.sendButton.addEventListener('click', () => this.sendMessage());
111
+
112
+ // Wire up the close button
113
+ this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => {
114
+ //this.closeChat()
115
+ this.closest('joe-ai-chatbox').closeChat();
116
+ });
117
+ // this.ui.assistant_select = this.shadowRoot.querySelector('#assistant-select');
118
+ // this.ui.assistant_select?.addEventListener('change', (e) => {
119
+ // this.selected_assistant_id = e.target.value;
120
+ // });
121
+ //this.selected_assistant_id = this.ui.assistant_select?.value || null;
122
+
123
+ }
124
+ updateMessages(messages){
125
+ messages && (this.messages = messages);
126
+ const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
127
+ this.UI.content.innerHTML = chatMessages;
128
+ this.UI.content.scrollTop = this.UI.content.scrollHeight;
31
129
  }
32
130
  async getAllAssistants() {
33
131
  const res = await fetch('/API/item/ai_assistant');
@@ -51,6 +149,7 @@
51
149
 
52
150
  }
53
151
  async loadConversation() {
152
+ //load conversation and messages into this
54
153
  try {
55
154
  const res = await fetch(`/API/object/ai_conversation/_id/${this.conversation_id}`);
56
155
  const convo = await res.json();
@@ -66,9 +165,11 @@
66
165
  const resThread = await fetch(`/API/plugin/chatgpt-assistants/getThreadMessages?thread_id=${convo.thread_id}`);
67
166
  const threadMessages = await resThread.json();
68
167
  this.messages = threadMessages?.messages || [];
168
+ this.thread_id = convo.thread_id;
169
+ this.user = $J.get(convo.user);
69
170
  }
70
171
 
71
- this.render();
172
+ return {conversation:convo,messages:this.messages};
72
173
  } catch (err) {
73
174
  console.error("Chatbox load error:", err);
74
175
  this.renderError("Error loading conversation.");
@@ -87,121 +188,40 @@
87
188
  const convoName = this.conversation.name || "Untitled Conversation";
88
189
  const convoInfo = this.conversation.info || "";
89
190
 
90
- const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
91
191
 
92
- const assistantOptions = (this.conversation.assistants || []).map(a => {
93
- const meta = this.getAssistantInfo(a) || {};
94
-
95
- const label = meta.name || a.name || a.title || 'Assistant';
96
- const value = meta._id;
97
- const selected = value === this.selected_assistant_id ? 'selected' : '';
98
-
99
- return `<option value="${value}" ${selected}>${label}</option>`;
100
- }).join('');
101
-
102
- const assistantSelect = `
103
- <label-select-wrapper>
104
- <label class="assistant-select-label" title="joe ai assistants">${_joe && _joe.SVG.icon.assistant}</label>
105
- <select id="assistant-select">
106
- ${assistantOptions}
107
- </select>
108
- </label-select-wrapper>
109
- `;
110
192
 
111
- this.shadowRoot.innerHTML = `
112
- <style>
113
-
114
- .chatbox {
115
- padding:10px; display:flex; flex-direction:column; height:100%;
116
- }
117
- .close-btn {
118
- position: absolute;
119
- top: 8px;
120
- right: 8px;
121
- width: 24px;
122
- height: 24px;
123
- cursor: pointer;
124
- }
125
- .close-btn svg { width:100%; height:100%; }
126
- .header { margin-bottom:10px; }
127
- .header h2 { margin:0; font-size:18px; }
128
- .header p { margin:4px 0 0 0; font-size:12px; color:#666; }
129
- .header svg{
130
- width: 36px;
131
- height: 36px;
132
- }
133
- label-select-wrapper{
134
- display: flex; align-items: center; gap: 8px;
135
- }
136
- .messages { flex:1; overflow:auto; max-height:300px; margin-bottom:10px;
137
- background: #fff;
138
- border: 1px solid #ccc;
139
- }
140
- .thinking-message {
141
- font-size: 13px;
142
- color: #888;
143
- font-style: italic;
144
- margin: 10px 0;
145
- text-align: center;
146
- }
147
- .meta {
148
- font-size: 14px;
149
- font-weight: bold;
150
- margin-bottom: 2px;
151
- display: flex;
152
- align-items: center;
153
- gap: 8px;
154
- color:#99999999;
155
- }
156
- .content {
157
- font-size: 14px;
158
- white-space: pre-wrap;
159
- }
160
- .inputRow { display:flex; gap:8px; }
161
- input[type="text"] { flex:1; padding:8px; border-radius:4px; border:1px solid #ccc; }
162
- button { padding:8px 12px; background:#007bff; color:white; border:none; border-radius:4px; cursor:pointer; }
163
- button:disabled { opacity:0.5; cursor:default; }
164
- .message { margin-bottom:10px; padding:8px; border-radius:6px; background:#f5f5f5; }
165
- .message.assistant { background:#eef; }
166
- .message.system { background:#fee; }
167
- </style>
168
-
169
- <div class="chatbox">
170
- <div class="close-btn" title="Close Chatbox">
171
- ${_joe.SVG.icon.close}
172
- </div>
173
- <div class="header">
174
- <h2>${convoName}</h2>
175
- <p>${convoInfo}</p>
176
- ${assistantSelect}
177
- </div>
178
- <div class="messages">
179
- ${chatMessages}
180
- </div>
181
- <div class="inputRow">
182
- <input id="chat-input" type="text" placeholder="Type a message..." />
183
- <button id="send-button">Send</button>
184
- </div>
185
- </div>
186
- `;
187
- const messagesDiv = this.shadowRoot.querySelector('.messages');
188
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
189
- this.shadowRoot.getElementById('send-button').addEventListener('click', () => this.sendMessage());
190
-
191
- // Wire up the close button
192
- this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => this.closeChat());
193
- this.ui.assistant_select = this.shadowRoot.querySelector('#assistant-select');
194
- this.ui.assistant_select?.addEventListener('change', (e) => {
195
- this.selected_assistant_id = e.target.value;
196
- });
197
- this.selected_assistant_id = this.ui.assistant_select?.value || null;
193
+
194
+
195
+
196
+
197
+
198
198
 
199
199
  }
200
-
200
+ processMessageText(text) {
201
+
202
+ // const replaced = text.replace(
203
+ // /\{\{\{BEGIN_OBJECT:(.*?)\}\}\}[\s\S]*?\{\{\{END_OBJECT:\1\}\}\}/g,
204
+ // (match, cuid) => `<joe-object object_id="${cuid}"></joe-object>`
205
+ // );
206
+ let didReplace = false;
207
+
208
+ const replaced = text.replace(
209
+ /\{\{\{BEGIN_OBJECT:(.*?)\}\}\}[\s\S]*?\{\{\{END_OBJECT:\1\}\}\}/g,
210
+ (match, cuid) => {
211
+ didReplace = true;
212
+ return `<joe-object object_id="${cuid}"></joe-object>`;
213
+ }
214
+ );
215
+
216
+ return {text:replaced, replaced:didReplace};
217
+ }
201
218
  renderMessage(msg) {
202
219
  const role = msg.role || 'user';
203
220
  const classes = `message ${role}`;
204
-
221
+ var username = role;
222
+ if(role === 'user'){
223
+ username = this.user.name||'User';
224
+ }
205
225
  let contentText = '';
206
226
 
207
227
  if (Array.isArray(msg.content)) {
@@ -217,30 +237,58 @@
217
237
  } else {
218
238
  contentText = '[Unsupported message format]';
219
239
  }
220
-
240
+
241
+ const ctInfo = this.processMessageText(contentText);
242
+ contentText = ctInfo.text;
243
+ if(ctInfo.replaced){
244
+ username = 'platform'
245
+ }
246
+
221
247
  // Build timestamp
222
248
  const createdAt = msg.created_at ? new Date(msg.created_at * 1000) : null; // OpenAI sends timestamps in seconds
223
249
  const timestamp = createdAt ? createdAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '';
224
250
 
225
251
  return `
226
252
  <div class="${classes}">
227
- <div class="meta"><strong>${role.toUpperCase()}</strong> ${timestamp ? `<span class="timestamp">${timestamp}</span>` : ''}</div>
253
+ <div class="meta">
254
+ <participant-name>${username.toUpperCase()}</participant-name>
255
+ ${timestamp ? `<span class="timestamp">${timestamp}</span>` : ''}</div>
228
256
  <div class="content">${contentText}</div>
229
257
  </div>
230
258
  `;
231
259
  }
232
260
 
233
261
  renderError(message) {
234
- this.shadowRoot.innerHTML = `<div style="color:red;">${message}</div>`;
262
+ this.shadowRoot.innerHTML = `<div style="color:red;">${message}</div>
263
+ <div class="close-btn" title="Close Chatbox" >${_joe.SVG.icon.close}</div>`;
264
+ this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => {
265
+ //this.closeChat()
266
+ this.closest('joe-ai-chatbox').closeChat();
267
+ });
235
268
  }
236
-
237
- async sendMessage() {
238
- const input = this.shadowRoot.getElementById('chat-input');
269
+
270
+
271
+ async getResponse(conversation_id,content,role,assistant_id){
272
+ const response = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
273
+ method: 'POST',
274
+ headers: { 'Content-Type': 'application/json' },
275
+ body: JSON.stringify({
276
+ conversation_id: conversation_id,
277
+ content: content,
278
+ role: role||'system',
279
+ assistant_id: assistant_id||Ai.default_ai?Ai.default_ai.value:null
280
+ })
281
+ }).then(res => res.json());
282
+ }
283
+
284
+ async sendMessage() {//** */
285
+
286
+ const input = this.UI.textarea;
239
287
  const message = input.value.trim();
240
288
  if (!message) return;
241
289
 
242
290
  input.disabled = true;
243
- this.shadowRoot.getElementById('send-button').disabled = true;
291
+ this.UI.sendButton.disabled = true;
244
292
 
245
293
  try {
246
294
  const response = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
@@ -256,7 +304,9 @@
256
304
  if (response.success && response.runObj) {
257
305
  this.currentRunId = response.runObj.id; // Store the run ID for polling
258
306
  await this.loadConversation(); // reload messages
307
+ this.UI.content.update(); // update messages
259
308
  this.startPolling(); // 🌸 start watching for assistant reply!
309
+ input.value = '';
260
310
  } else {
261
311
  alert('Failed to send message.');
262
312
  }
@@ -265,9 +315,9 @@
265
315
  alert('Error sending message.');
266
316
  }
267
317
 
268
- input.value = '';
269
- input.disabled = false;
270
- this.shadowRoot.getElementById('send-button').disabled = false;
318
+
319
+ // input.disabled = false;
320
+ // this.shadowRoot.getElementById('send-button').disabled = false;
271
321
  }
272
322
 
273
323
  startPolling() {
@@ -286,18 +336,21 @@
286
336
  const threadMessages = await resThread.json();
287
337
 
288
338
  if (threadMessages?.messages) {
289
- this.messages = threadMessages.messages;
290
- this.render();
339
+ this.UI.content.update(threadMessages.messages);
340
+ //this.messages = threadMessages.messages;
341
+ //this.render();
291
342
  }
292
343
 
293
344
  clearInterval(this.pollingInterval);
294
345
  this.pollingInterval = null;
295
346
  this.hideThinkingMessage();
347
+ this.UI.textarea.disabled = false;
348
+ this.UI.sendButton.disabled = false;
296
349
  }
297
350
  }, 2000);
298
351
  }
299
352
  showThinkingMessage() {
300
- const messagesDiv = this.shadowRoot.querySelector('.messages');
353
+ const messagesDiv = this.UI.content;
301
354
  if (!messagesDiv) return;
302
355
 
303
356
  // Pull assistant thinking text
@@ -329,14 +382,69 @@
329
382
  }
330
383
 
331
384
  customElements.define('joe-ai-chatbox', JoeAIChatbox);
332
-
385
+
386
+
387
+ class JoeObject extends HTMLElement {
388
+ constructor() {
389
+ super();
390
+
391
+
392
+ this.object_id = this.getAttribute('object_id');
393
+ this.object = $J.get(this.object_id);
394
+
395
+ }
396
+ connectedCallback() {
397
+ const id = this.getAttribute('object_id');
398
+ var sTemp = $J.schema('business')?.listView?.title||false;
399
+ this.innerHTML = (sTemp)?JOE.propAsFuncOrValue(sTemp,this.object) :`<jo-title>${this.object.name}</jo-title>
400
+ <jo-subtitle>${this.object.info} - ${this.object._id}</jo-subtitle>`;
401
+ this.addEventListener('click', () => {
402
+ // Handle click event here, e.g., open the object in a new tab or show details
403
+ goJoe(_joe.search(this.object._id)[0],{schema:this.object.itemtype})
404
+ //window.open(`/object/${this.object_id}`, '_blank');
405
+ });
406
+ }
407
+
408
+ }
409
+ customElements.define('joe-object', JoeObject);
410
+
411
+
412
+ //**YES**
413
+ Ai.spawnChatHelper = async function(object_id,user_id=_joe.User._id,conversation_id) {
414
+ //if not conversation_id, create a new one
415
+ let convo_id = conversation_id;
416
+ var newChat = false;
417
+ if(!convo_id){
418
+ const response = await fetch('/API/plugin/chatgpt-assistants/createConversation', {
419
+ method: 'POST',
420
+ headers: { 'Content-Type': 'application/json' },
421
+ body: JSON.stringify({
422
+ object_id,
423
+ user_id
424
+ })
425
+ }).then(res => res.json());
426
+ convo_id = response?.conversation?._id;
427
+ newChat = true;
428
+ if(response.error){
429
+ console.error('❌ Failed to create conversation:', response.error);
430
+ return;
431
+ }
432
+ }
433
+
434
+ await _joe.Ai.spawnContextualChat(convo_id,{object_id,newChat});
435
+ }
436
+ Ai.getDefaultAssistant = function() {
437
+
438
+ Ai.default_ai = Ai.default_ai ||_joe.Data.setting.where({name:'DEFAULT_AI_ASSISTANT'})[0]||false;
439
+ return Ai.default_ai;
440
+ }
333
441
  // ========== HELPERS ==========
334
442
  Ai.spawnContextualChat = async function(conversationId, options = {}) {
335
443
  if (!conversationId) {
336
444
  console.warn("Missing conversation ID for chat spawn.");
337
445
  return;
338
446
  }
339
- Ai.default_ai = _joe.Data.setting.where({name:'DEFAULT_AI_ASSISTANT'})[0]||false;
447
+ Ai.getDefaultAssistant();
340
448
 
341
449
  // 1. Check if chat already open
342
450
  if (Ai._openChats[conversationId]) {
@@ -346,9 +454,11 @@
346
454
  }
347
455
 
348
456
  try {
457
+ const flattened = _joe.Object.flatten(options.object_id);
458
+ if (options.newChat) {
349
459
  // 2. Prepare context
350
- const flattened = _joe.Object.flatten();
351
- const contextInstructions = _joe.Ai.generateContextInstructions(flattened);
460
+
461
+ const contextInstructions = _joe.Ai.generateContextInstructions(flattened,options.object_id);
352
462
 
353
463
  // 3. Inject context into backend
354
464
  const contextResult = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
@@ -356,9 +466,10 @@
356
466
  headers: { 'Content-Type': 'application/json' },
357
467
  body: JSON.stringify({
358
468
  conversation_id: conversationId,
359
- //role: 'system',
469
+ role: 'JOE',
360
470
  content: contextInstructions,
361
- assistant_id: Ai.default_ai.value
471
+ assistant_id: Ai.default_ai.value,
472
+ object_id: options.object_id
362
473
  })
363
474
  }).then(res => res.json());
364
475
 
@@ -366,38 +477,51 @@
366
477
  console.error('❌ Failed to inject context:', contextResult?.error);
367
478
  return;
368
479
  }
369
-
480
+ }
370
481
  // 4. Create new chatbox
371
482
  const chat = document.createElement('joe-ai-chatbox');
372
483
  chat.setAttribute('conversation_id', conversationId);
373
-
484
+ const screenWidth = window.innerWidth;
485
+ if(screenWidth <= 768){
486
+ chat.setAttribute('mobile', 'true');
487
+ chat.style.width = 'auto';
488
+ chat.style.left = '0px';
489
+ }
490
+ else{
491
+ chat.setAttribute('mobile', 'false');
492
+ chat.style.width = options.width || '640px';
493
+ chat.style.left = 'auto';
494
+ }
374
495
  // Apply styles
375
- chat.style.width = options.width || '400px';
376
- chat.style.height = options.height || '420px';
377
- chat.style.bottom = options.bottom || '20px';
378
- chat.style.right = options.right || '20px';
496
+
497
+ //chat.style.height = options.height || '420px';
498
+ chat.style.bottom = options.bottom || '50px';
499
+ chat.style.right = options.right || '0px';
500
+ chat.style.top = options.top || '50px';
379
501
  chat.style.position = 'fixed';
380
502
  chat.style.zIndex = '10000';
381
503
  chat.style.background = '#efefef';
382
504
  chat.style.border = '1px solid #fff';
383
505
  chat.style.borderRadius = '8px';
384
- chat.style.boxShadow = '0px 2px 10px rgba(0,0,0,0.1)';
506
+ chat.style.boxShadow = '0px 1px 4px rgba(0, 0, 0, 0.3)';
385
507
  chat.style.padding = '5px';
386
-
508
+ chat.style.margin = '5px';
509
+
387
510
  document.body.appendChild(chat);
388
511
 
389
512
  // 5. Track it
390
513
  Ai._openChats[conversationId] = chat;
391
514
 
515
+ if (options.newChat) {
392
516
  // 6. Show soft local UI message
393
- _joe.Ai.injectSystemMessage(conversationId, `Context injected: ${flattened.name || flattened.title || 'Object'} (${flattened._id})`);
394
-
517
+ //_joe.Ai.injectSystemMessage(conversationId, `Context injected: ${flattened.name || flattened.title || 'Object'} (${flattened._id})`);
518
+ }
395
519
  } catch (err) {
396
520
  console.error('❌ spawnChat context injection failed:', err);
397
521
  }
398
522
  };
399
523
 
400
- Ai.spawnChat = function(conversationId, options = {}) {
524
+ /* Ai.spawnChat = function(conversationId, options = {}) {
401
525
  if (!conversationId) {
402
526
  console.warn("Missing conversation ID for chat spawn.");
403
527
  return;
@@ -442,27 +566,31 @@
442
566
  return chat;
443
567
  // 4. Optionally clean up when chatbox is removed (if you wire close buttons later)
444
568
  };
445
-
446
- Ai.generateContextInstructions = function(flattenedObj) {
569
+ */
570
+ Ai.generateContextInstructions = function(flattenedObj,object_id) {
447
571
  if (!flattenedObj) return '';
448
572
 
449
- let context = "Context: You are assisting the user with the following object:\n\n";
450
-
451
- for (const [key, value] of Object.entries(flattenedObj)) {
452
- if (typeof value === 'object' && value !== null) {
453
- context += `- ${key}: (linked object)\n`;
454
- for (const [subkey, subval] of Object.entries(value)) {
455
- context += ` • ${subkey}: ${subval}\n`;
456
- }
457
- } else {
458
- context += `- ${key}: ${value}\n`;
459
- }
460
- }
573
+ let context = `{{{BEGIN_OBJECT:${object_id}}}}`+
574
+ "Context: You are assisting the user with the following object:\n\n";
575
+
576
+ context += JSON.stringify(flattenedObj, null, 2) + "\n\n";
577
+ // for (const [key, value] of Object.entries(flattenedObj)) {
578
+ // if (typeof value === 'object' && value !== null) {
579
+ // context += `- ${key}: (linked object)\n`;
580
+ // for (const [subkey, subval] of Object.entries(value)) {
581
+ // context += ` • ${subkey}: ${subval}\n`;
582
+ // }
583
+ // } else {
584
+ // context += `- ${key}: ${value}\n`;
585
+ // }
586
+ // }
461
587
 
462
- context += `\nAlways refer to this context when answering questions or completing tasks related to this object.\n`;
588
+ context += `\nAlways refer to this context when answering questions or completing tasks related to this object.\n`+
589
+ `{{{END_OBJECT:${object_id}}}}`;
463
590
 
464
591
  return context;
465
592
  };
593
+
466
594
  Ai.injectSystemMessage = async function(conversationId, text) {
467
595
  if (!conversationId || !text) return;
468
596
 
@@ -470,7 +598,7 @@
470
598
  // Create a system-style message object
471
599
  const messageObj = {
472
600
  conversation_id: conversationId,
473
- role: 'system',
601
+ role: 'joe',
474
602
  content: text,
475
603
  created: new Date().toISOString()
476
604
  };