json-object-editor 0.10.654 → 0.10.657

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.
@@ -1,325 +1,390 @@
1
- var schema = {
2
- display:'Ai Prompt',
3
- title : 'Ai Prompt | ${name}',
4
- info:"An ai prompt that can be sent to openAi, etc",
5
- summary:{
6
- description:'Reusable AI prompt definition that describes how to call the chatgpt plugin or Responses API.',
7
- purpose:'Use ai_prompt to define prompt methods, helper functions, and instructions for generating structured AI responses or content. Prompts can be tagged, scoped to datasets, and linked to ai_response records.',
8
- labelField:'name',
9
- defaultSort:{ field:'joeUpdated', dir:'desc' },
10
- searchableFields:['name','info','prompt_method','ai_model','tags','datasets'],
11
- allowedSorts:['joeUpdated','created','name','ai_model'],
12
- relationships:{
13
- outbound:[
14
- { field:'status', targetSchema:'status', cardinality:'one' },
15
- { field:'tags', targetSchema:'tag', cardinality:'many' },
16
- { field:'datasets', targetSchema:'<schemaName>', cardinality:'many' }
17
- ],
18
- inbound:{ graphRef:'server/relationships.graph.json' }
19
- },
20
- joeManagedFields:['created','joeUpdated'],
21
- fields:[
22
- { name:'_id', type:'string', required:true },
23
- { name:'itemtype', type:'string', required:true, const:'ai_prompt' },
24
- { name:'name', type:'string', required:true },
25
- { name:'info', type:'string' },
26
- { name:'prompt_method', type:'string' },
27
- { name:'content_items', type:'objectList' },
28
- { name:'functions', type:'string' },
29
- { name:'instructions_format', type:'string' },
30
- { name:'instructions', type:'string' },
31
- { name:'user_prompt', type:'string' },
32
- { name:'attachments_mode', type:'string', display:'Attachments Mode', enumValues:['direct','file_search'], default:'direct' },
33
- { name:'status', type:'string', isReference:true, targetSchema:'status' },
34
- { name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' },
35
- { name:'ai_model', type:'string' },
36
- { name:'temperature', type:'number' },
37
- { name:'datasets', type:'string', isArray:true },
38
- { name:'joeUpdated', type:'string', format:'date-time' },
39
- { name:'created', type:'string', format:'date-time' }
40
- ]
41
- },
42
- menuicon:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="-128 -128 1280 1280">
43
- <path d="M356.52 692.84v31.84c0 11.07.47 17.51 1.4 19.31 1.19 2.44 3.42 4.2 6.07 4.8 4.53 1.05 9.18 1.49 13.83 1.31h8.55c9.29 0 13.93 2.98 13.93 8.94 0 6.89-4.9 11.11-14.71 12.66-12.05 1.35-24.18 1.91-36.31 1.66-14.75 0-25.19-2.86-31.32-8.58-6.13-5.72-9.21-15.51-9.23-29.36v-40.03c.42-5.59-.57-11.2-2.87-16.31-2.81-3.78-6.95-6.36-11.58-7.21-5.48-1.37-9.15-6.51-8.68-12.14-.53-5.64 3.17-10.81 8.68-12.14 4.65-.96 8.77-3.61 11.58-7.44 2.32-5.11 3.31-10.72 2.87-16.31v-39.93c0-13.64 3.07-23.32 9.2-29.04 6.13-5.72 16.57-8.57 31.32-8.55 12.9-.36 25.81.33 38.6 2.06 3.42.52 6.6 2.05 9.14 4.4 2.11 2.02 3.29 4.82 3.26 7.73 0 5.96-4.64 8.94-13.93 8.94h-8.81c-7.92 0-13.35.85-16.31 2.54-2.92 1.96-4.62 5.29-4.47 8.81v45.77c-.17 15.42-4.31 25.69-12.4 30.8-.64.43-1.02 1.16-1.01 1.92 0 1.2 1.45 2.99 4.34 5.38 3.23 2.94 5.58 6.73 6.79 10.93 1.58 5.6 2.28 11.41 2.09 17.23Zm90.28-100.23c16.42.07 29.72 13.37 29.79 29.79.25 8.33-3.05 16.37-9.07 22.12-11.82 11.24-30.37 11.24-42.18 0-6.02-5.75-9.32-13.79-9.07-22.12-.18-7.36 2.58-14.49 7.67-19.8 5.8-6.5 14.16-10.15 22.87-9.98Zm0 73.9c16.42.07 29.72 13.37 29.79 29.79.25 8.34-3.06 16.4-9.1 22.15-11.82 11.24-30.37 11.24-42.18 0-6.02-5.75-9.32-13.79-9.07-22.12-.18-7.36 2.58-14.49 7.67-19.8 5.8-6.52 14.17-10.18 22.9-10.02Zm89.36-40.03v-31.71c0-11.07-.47-17.51-1.4-19.31-1.19-2.44-3.42-4.2-6.07-4.8-4.53-1.05-9.18-1.49-13.83-1.31h-8.48c-9.29 0-13.93-2.98-13.93-8.94 0-6.89 4.9-11.07 14.71-12.53 12.05-1.35 24.18-1.9 36.31-1.66 14.75 0 25.19 2.86 31.32 8.58 6.13 5.72 9.2 15.4 9.2 29.04v40.13c-.44 5.59.55 11.2 2.87 16.31 2.84 3.76 6.96 6.35 11.58 7.28 5.51 1.34 9.2 6.53 8.65 12.17.37 4.53-1.99 8.85-6 10.99-2.02.92-4.11 1.65-6.26 2.19-4.2 1.33-7.58 4.48-9.2 8.58-1.34 4.52-1.9 9.23-1.66 13.93v40c0 13.72-3.07 23.44-9.2 29.13-6.13 5.7-16.57 8.56-31.32 8.58-12.9.36-25.81-.32-38.6-2.06-3.42-.52-6.6-2.05-9.14-4.4-2.13-2.06-3.31-4.9-3.26-7.86 0-5.96 4.64-8.94 13.93-8.94 1.11 0 4.05.09 8.81.26 5.55.28 11.11-.59 16.31-2.54 2.02-1.05 3.47-2.95 3.92-5.19.66-5.88.9-11.8.72-17.72v-31.84c-.25-6.36.68-12.71 2.74-18.73 2.07-4.82 5.46-8.96 9.79-11.94.64-.43 1.02-1.16 1.01-1.92 0-1.2-1.45-2.99-4.34-5.38-3.23-2.94-5.58-6.73-6.79-10.93-1.69-5.66-2.5-11.55-2.38-17.45Zm164.47-327.03 197.56 40.87-197.56 40.86-40.86 197.57-40.87-197.57-197.56-40.86 197.56-40.87 40.87-197.56 40.86 197.56z"/>
44
- <path d="m516.05 158.53 81.14 16.79-81.14 16.78-16.78 81.15-16.79-81.15-81.14-16.78 81.14-16.79 16.79-81.14 16.78 81.14zm43.7 281.88 41.02 8.48-41.02 8.48-8.48 41.02-8.49-41.02-41.01-8.48 41.01-8.48 8.49-41.02 8.48 41.02z"/>
45
- <path d="M698.87 563.96c16.22 26.17 25.29 55.26 25.29 85.9 0 117.09-105.49 205.38-245.39 205.38-3.01 0-6.03-.04-9.07-.12-13.15-.34-27.52-2.1-42.74-3.96-16.83-2.06-34.23-4.19-52.19-4.67l-8.81-.24-8.65 1.71-138.94 27.52 12.32-30.18 18.2-44.57-32.74-35.61c-22.03-23.96-48.3-63.4-48.3-115.27 0-113.76 124.78-206.3 278.15-206.3 10.26 0 20.38.43 30.35 1.23l-48.12-48.12C259.65 403.99 125.8 514.97 125.8 650.82c0 56.11 22.89 107.94 61.57 150.01l-59.5 145.77 252.16-49.95c26.34.7 55.86 6.99 85.42 7.76 3.35.09 6.69.13 10.01.13 171.44 0 290.73-115.81 290.73-253.72 0-42.53-13.14-82.62-36.34-117.86l-30.99 30.99Z"/>
46
- </svg>`,
47
- listView:{
48
- title: function(aip){
49
- return `
50
- <joe-title>${aip.name}</joe-title>
51
- <joe-subtitle>${aip.info}</joe-subtitle><br/>
52
-
53
- <joe-subtext class="fright"><div><b>${aip.ai_model}</b></div>${aip.prompt_method}</joe-subtext>
54
- <joe-subtext>${aip.content_items.map(i=>{
55
- return `<b>${i.itemtype}</b>: ${i.reference}`;
56
- }).join('')}</joe-subtext>
57
- ${_joe.schemas.tag.methods.renderTags(aip.tags,'fleft')}
58
- `;
59
- },
60
- listWindowTitle: 'Ai Prompts'
61
- },
62
- subsets:function(i){
63
-
64
- return _joe.Filter.Options.status();
65
- },
66
- filters:function(i){
67
-
68
- return _joe.Filter.Options.tags();
69
- },
70
- stripeColor: function(item) {
71
- //use the stripe color from ai_assistant status object if it has one
72
- if (item.status){
73
- var status = _joe.Cache.get(item.status);
74
- if (status && status.color) {
75
- return {color:status.color,title:status.name};
76
- }
77
- }
78
- },
79
- onload:function(dataset){
80
- //replace the blank instructions field with the populated template field && the blank instrctions format field with the populated template type field
81
- dataset.map(function(item){
82
- if(item.template){
83
- item.instructions = item.template;
84
- delete item.template;
85
- }
86
- if(item.template_type){
87
- item.instructions_format = item.template_type;
88
- delete item.template_type;
89
- }
90
- })
91
- },
92
- fields:[
93
- 'name',
94
- 'info',
95
- {name:"prompt_method",placeholder:"name of method to call in plugin", comment:'use executeJOEAiPrompt to use the JOE ui here for all smarts', default:"executeJOEAiPrompt",display:"Prompt Plugin Method",type:'text'},
96
- {name:'content_items',type:'objectList',
97
- properties:['itemtype','reference']
98
- },
99
- {section_start:'Input'},
100
- {name: 'functions', type: 'code', display: 'Helper Functions', language:'javascript',comment: `
101
- <div>AI Prompt Helper Function
102
-
103
- <h4>Usage:</h4>
104
- <ul>
105
- <li>Must export an async function.</li>
106
- <li>Receives: { instructions, params, ai_prompt } as a single argument.</li>
107
- <li>Must return a final modified instructions string.</li>
108
- </ul>
109
-
110
- <h4>Example:</h4>
111
- <div class="pad10">
112
- <pre>
113
- module.exports = async function({ instructions, params, ai_prompt })
114
- // Your code here to modify the instructions based off params
115
- const object = $J.get(params.idToLookup);
116
-
117
- trackObject(params.idToLookup); // Tell parent we used this
118
-
119
- return {
120
- instructions: "Updated system-level instructions string",
121
- input: "Specific input text (URL, query, etc)"
122
- };
123
-
124
- };
125
- </pre>
126
- </div>
127
- </div>`},
128
- {section_end:'Input'},
129
- {section_start:'Instruct'},
130
- {label:'Instructions'},
131
- "instructions_format",
132
- "instructions",
133
- {name:"user_prompt",display:'User Prompt',type:'rendering',comment:'speciific input to the prompt if any, such as campaign intent'},
134
-
135
- // 'template_type',
136
- // {extend:'template',specs:{comment:'legacy field, use instructions instead',
137
- // // comment:`<div>use $\\{this.AI_PROMPT.name\\} $\\{this.CONTENT\\} <br/></div><div>for <b>module</b>: export function(data) that returns an html string.
138
- // // data.request, data.REPORT,
139
- // // <br/>shorthand: $J.get(itemid) $J.schema(schemaname)</div>`,
140
- // language:function(item){
141
- // switch(item.template_type){
142
- // case 'module':
143
- // return 'javascript';
144
- // break;
145
- // default:
146
- // return '';
147
- // }
148
-
149
- // }
150
- // }},
151
- {section_end:'Instruct'},
152
-
153
- {section_start:'system',collapsed:true},
154
- '_id','created','itemtype',
155
- {section_end:'system'},
156
- {sidebar_start:'right',collapsed:true},
157
- {section_start:'workflow'},
158
- 'status',
159
- 'tags',
160
- // {name:'merge_status',comment:'status to add when this data is merged in', allowMultiple:false,type:"objectReference",display:'Merge Status',values:function(){
161
- // return _joe.getDataset('status');
162
- // }},
163
- {section_end:'workflow'},
164
- {section_start:'openAi',collapsed:true},
165
- 'ai_model',
166
- {name:'attachments_mode', type:'select', display:'Attachments Mode', values:['direct','file_search'], default:'direct', comment:'direct = include files in prompt as context; file_search = index files and retrieve relevant chunks'},
167
- {name:'temperature', type:'number',display:'Temperature', default:.7, step:"0.1",comment:'0-1, 0 is deterministic, 1 is random'},
168
- //{name:'max_tokens', type:'number',display:'Max Tokens',comment:'max tokens to return',default:4096},
169
- {section_end:'openAi'},
170
-
171
-
172
- {section_start:'examples',collapsed:true},
173
- {name:'examples', display:'prompt examples', comment:'runs the prompt to create a response',type:'content', reloadable:true,run:function(item){
174
- return $J.schema('ai_prompt').methods.listExamples(item);
175
- } },
176
- {section_end:'examples'},
177
-
178
- {sidebar_end:'right'},
179
- {sidebar_start:'left',collapsed:function(aip){
180
- return aip.datasets.length != 0;
181
- }},
182
- {section_start:'categorization',collapsed:false},
183
- 'datasets',
184
- {section_end:'categorization'},
185
- {sidebar_end:'left'},
186
- ],
187
- idprop : "_id",
188
- sorter:['name','!joeUpdated'],
189
- methods:{
190
- buildURL: function(prompt, items = []) {
191
- const method = prompt.prompt_method || '';
192
- let url = `/API/plugin/chatgpt/${method}?ai_prompt=${prompt._id}`;
193
-
194
- if (!Array.isArray(items)) {
195
- console.warn('Expected items to be an array.');
196
- return url;
197
- }
198
-
199
- if (prompt.content_items) {
200
- prompt.content_items.forEach(ci => {
201
- const matchingItem = items.find(it => it.itemtype === ci.itemtype );
202
- if (matchingItem) {
203
- url += `&${ci.reference}=${matchingItem._id}`;
204
- }
205
- });
206
- }
207
-
208
- return url;
209
- },
210
- // listItemClickHandler:function(itemid,promptid){
211
- // var item = $J.get(itemid);
212
- // var prompt;
213
- // if(promptid){
214
- // prompt = $J.get(promptid);
215
- // }else{
216
- // prompt = _jco();
217
- // }
218
- // window.open(this.buildURL(prompt,[item]));
219
- // },
220
- listItemClickHandler: function (itemMap, promptId) {
221
- const prompt = promptId ? $J.get(promptId,'ai_prompt') : _jco();
222
- const items = [];
223
-
224
- for (const [itemtype, id] of Object.entries(itemMap)) {
225
- const obj = $J.get(id,itemtype);
226
- if (obj) {
227
- items.push(obj);
228
- }
229
- }
230
-
231
- window.open(this.buildURL(prompt, items));
232
- },
233
- listExamples:function(prompt){
234
- var examples = [];
235
- var html ='';
236
- var ref0;
237
- var it0;
238
- if(!prompt.content_items){
239
- return 'select content items to send with this prompt';
240
- }
241
- prompt.content_items.map(function(ci){
242
- if(_joe.Data[ci.itemtype]){
243
- examples = examples.concat(_joe.Data[ci.itemtype]);
244
- it0 = ci.itemtype;
245
- ref0 = ci.reference;
246
- }
247
- })
248
- var finalExamples = [];
249
-
250
- while(finalExamples.length < 5 && finalExamples.length < examples.length){
251
- let ci = examples[Math.floor(Math.random()*examples.length)];
252
- if(finalExamples.indexOf(ci) == -1){
253
- finalExamples.push(ci);
254
- }
255
- }
256
- var rep_url;
257
- var method =
258
- finalExamples.map(function(ex){
259
- //rep_url = '/API/plugin/chatgpt'+'?ai_prompt='+prompt._id+'&'+ref0+'='+ex._id;
260
- html += _joe.renderFieldListItem(ex,
261
- '<joe-subtext>'+it0+'</joe-subtext>'+
262
- '<joe-title>${name} ${date}</joe-title>\
263
- <joe-subtext>${website}</joe-subtext>',
264
- it0,
265
- {
266
- //action:'onclick="window.open(\''+rep_url+'\');"',
267
- action:`onclick="_joe.schemas.ai_prompt.methods.listItemClickHandler('${ex._id}');"`,
268
- icon:'ai_prompt'
269
- }
270
- );
271
- })
272
-
273
- return html;
274
- },
275
- listPrompts: function (objectMap) {
276
- if (!objectMap || typeof objectMap !== 'object') return '';
277
-
278
- const activeStatuses = _joe.getDataset('status')
279
- .filter(f => f.datasets.includes('ai_prompt') && f.active)
280
- .map(f => f._id);
281
-
282
- const prompts = _joe.getDataset('ai_prompt').filter(prompt =>
283
- activeStatuses.includes(prompt.status)
284
- );
285
-
286
- const context_items = {}
287
- // Inline helper: reduce objects to { itemtype: _id }
288
- const extractIds = map => {
289
- const idMap = {};
290
- for (const [type, obj] of Object.entries(map)) {
291
- if (obj && obj._id) idMap[type] = obj._id;
292
- }
293
- return idMap;
294
- };
295
-
296
- const objectIdMap = extractIds(objectMap);
297
- const paramStr = JSON.stringify(objectIdMap).replace(/"/g, '&quot;');
298
-
299
- let html = '';
300
- const temp = '<joe-title>${name}</joe-title>';
301
-
302
-
303
- let itemtype = _joe.current.object.itemtype;
304
- const matchingPrompts = prompts.filter(prompt =>
305
- (prompt.content_items || []).some(ci => ci.itemtype === itemtype)
306
- );
307
-
308
- matchingPrompts.forEach(prompt => {
309
- html += _joe.renderFieldListItem(prompt, temp, 'ai_prompt', {
310
- icon: 'ai_prompt',
311
- action: `onclick="(function(){ _joe.schemas.ai_prompt.methods.listItemClickHandler(${paramStr}, '${prompt._id}'); })()"`
312
-
313
- //action: `onclick="_joe.schemas.ai_prompt.methods.listItemClickHandler(${paramStr}, "${prompt._id}");"`
314
- });
315
- });
316
-
317
-
318
- return html;
319
- }
320
-
321
-
322
- }
323
- };
324
-
1
+ var schema = {
2
+ display:'Ai Prompt',
3
+ title : 'Ai Prompt | ${name}',
4
+ info:"An ai prompt that can be sent to openAi, etc",
5
+ summary:{
6
+ description:'Reusable AI prompt definition that describes how to call the chatgpt plugin or Responses API.',
7
+ purpose:'Use ai_prompt to define prompt methods, helper functions, and instructions for generating structured AI responses or content. Prompts can be tagged, scoped to datasets, and linked to ai_response records.',
8
+ labelField:'name',
9
+ defaultSort:{ field:'joeUpdated', dir:'desc' },
10
+ searchableFields:['name','info','prompt_method','ai_model','tags','datasets'],
11
+ allowedSorts:['joeUpdated','created','name','ai_model'],
12
+ relationships:{
13
+ outbound:[
14
+ { field:'status', targetSchema:'status', cardinality:'one' },
15
+ { field:'tags', targetSchema:'tag', cardinality:'many' },
16
+ { field:'datasets', targetSchema:'<schemaName>', cardinality:'many' }
17
+ ],
18
+ inbound:{ graphRef:'server/relationships.graph.json' }
19
+ },
20
+ joeManagedFields:['created','joeUpdated'],
21
+ fields:[
22
+ { name:'_id', type:'string', required:true },
23
+ { name:'itemtype', type:'string', required:true, const:'ai_prompt' },
24
+ { name:'name', type:'string', required:true },
25
+ { name:'info', type:'string' },
26
+ { name:'prompt_method', type:'string' },
27
+ { name:'content_items', type:'objectList' },
28
+ { name:'functions', type:'string' },
29
+ { name:'instructions_format', type:'string' },
30
+ { name:'instructions', type:'string' },
31
+ { name:'user_prompt', type:'string' },
32
+ { name:'attachments_mode', type:'string', display:'Attachments Mode', enumValues:['direct','file_search'], default:'direct' },
33
+ // MCP configuration (schema-level, used by executeJOEAiPrompt + runWithTools)
34
+ { name:'mcp_enabled', type:'boolean' },
35
+ { name:'mcp_toolset', type:'string' },
36
+ { name:'mcp_selected_tools', type:'string', isArray:true },
37
+ { name:'mcp_instructions_mode', type:'string' },
38
+ { name:'status', type:'string', isReference:true, targetSchema:'status' },
39
+ { name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' },
40
+ { name:'ai_model', type:'string' },
41
+ { name:'temperature', type:'number' },
42
+ { name:'datasets', type:'string', isArray:true },
43
+ { name:'joeUpdated', type:'string', format:'date-time' },
44
+ { name:'created', type:'string', format:'date-time' }
45
+ ]
46
+ },
47
+ menuicon:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="-128 -128 1280 1280">
48
+ <path d="M356.52 692.84v31.84c0 11.07.47 17.51 1.4 19.31 1.19 2.44 3.42 4.2 6.07 4.8 4.53 1.05 9.18 1.49 13.83 1.31h8.55c9.29 0 13.93 2.98 13.93 8.94 0 6.89-4.9 11.11-14.71 12.66-12.05 1.35-24.18 1.91-36.31 1.66-14.75 0-25.19-2.86-31.32-8.58-6.13-5.72-9.21-15.51-9.23-29.36v-40.03c.42-5.59-.57-11.2-2.87-16.31-2.81-3.78-6.95-6.36-11.58-7.21-5.48-1.37-9.15-6.51-8.68-12.14-.53-5.64 3.17-10.81 8.68-12.14 4.65-.96 8.77-3.61 11.58-7.44 2.32-5.11 3.31-10.72 2.87-16.31v-39.93c0-13.64 3.07-23.32 9.2-29.04 6.13-5.72 16.57-8.57 31.32-8.55 12.9-.36 25.81.33 38.6 2.06 3.42.52 6.6 2.05 9.14 4.4 2.11 2.02 3.29 4.82 3.26 7.73 0 5.96-4.64 8.94-13.93 8.94h-8.81c-7.92 0-13.35.85-16.31 2.54-2.92 1.96-4.62 5.29-4.47 8.81v45.77c-.17 15.42-4.31 25.69-12.4 30.8-.64.43-1.02 1.16-1.01 1.92 0 1.2 1.45 2.99 4.34 5.38 3.23 2.94 5.58 6.73 6.79 10.93 1.58 5.6 2.28 11.41 2.09 17.23Zm90.28-100.23c16.42.07 29.72 13.37 29.79 29.79.25 8.33-3.05 16.37-9.07 22.12-11.82 11.24-30.37 11.24-42.18 0-6.02-5.75-9.32-13.79-9.07-22.12-.18-7.36 2.58-14.49 7.67-19.8 5.8-6.5 14.16-10.15 22.87-9.98Zm0 73.9c16.42.07 29.72 13.37 29.79 29.79.25 8.34-3.06 16.4-9.1 22.15-11.82 11.24-30.37 11.24-42.18 0-6.02-5.75-9.32-13.79-9.07-22.12-.18-7.36 2.58-14.49 7.67-19.8 5.8-6.52 14.17-10.18 22.9-10.02Zm89.36-40.03v-31.71c0-11.07-.47-17.51-1.4-19.31-1.19-2.44-3.42-4.2-6.07-4.8-4.53-1.05-9.18-1.49-13.83-1.31h-8.48c-9.29 0-13.93-2.98-13.93-8.94 0-6.89 4.9-11.07 14.71-12.53 12.05-1.35 24.18-1.9 36.31-1.66 14.75 0 25.19 2.86 31.32 8.58 6.13 5.72 9.2 15.4 9.2 29.04v40.13c-.44 5.59.55 11.2 2.87 16.31 2.84 3.76 6.96 6.35 11.58 7.28 5.51 1.34 9.2 6.53 8.65 12.17.37 4.53-1.99 8.85-6 10.99-2.02.92-4.11 1.65-6.26 2.19-4.2 1.33-7.58 4.48-9.2 8.58-1.34 4.52-1.9 9.23-1.66 13.93v40c0 13.72-3.07 23.44-9.2 29.13-6.13 5.7-16.57 8.56-31.32 8.58-12.9.36-25.81-.32-38.6-2.06-3.42-.52-6.6-2.05-9.14-4.4-2.13-2.06-3.31-4.9-3.26-7.86 0-5.96 4.64-8.94 13.93-8.94 1.11 0 4.05.09 8.81.26 5.55.28 11.11-.59 16.31-2.54 2.02-1.05 3.47-2.95 3.92-5.19.66-5.88.9-11.8.72-17.72v-31.84c-.25-6.36.68-12.71 2.74-18.73 2.07-4.82 5.46-8.96 9.79-11.94.64-.43 1.02-1.16 1.01-1.92 0-1.2-1.45-2.99-4.34-5.38-3.23-2.94-5.58-6.73-6.79-10.93-1.69-5.66-2.5-11.55-2.38-17.45Zm164.47-327.03 197.56 40.87-197.56 40.86-40.86 197.57-40.87-197.57-197.56-40.86 197.56-40.87 40.87-197.56 40.86 197.56z"/>
49
+ <path d="m516.05 158.53 81.14 16.79-81.14 16.78-16.78 81.15-16.79-81.15-81.14-16.78 81.14-16.79 16.79-81.14 16.78 81.14zm43.7 281.88 41.02 8.48-41.02 8.48-8.48 41.02-8.49-41.02-41.01-8.48 41.01-8.48 8.49-41.02 8.48 41.02z"/>
50
+ <path d="M698.87 563.96c16.22 26.17 25.29 55.26 25.29 85.9 0 117.09-105.49 205.38-245.39 205.38-3.01 0-6.03-.04-9.07-.12-13.15-.34-27.52-2.1-42.74-3.96-16.83-2.06-34.23-4.19-52.19-4.67l-8.81-.24-8.65 1.71-138.94 27.52 12.32-30.18 18.2-44.57-32.74-35.61c-22.03-23.96-48.3-63.4-48.3-115.27 0-113.76 124.78-206.3 278.15-206.3 10.26 0 20.38.43 30.35 1.23l-48.12-48.12C259.65 403.99 125.8 514.97 125.8 650.82c0 56.11 22.89 107.94 61.57 150.01l-59.5 145.77 252.16-49.95c26.34.7 55.86 6.99 85.42 7.76 3.35.09 6.69.13 10.01.13 171.44 0 290.73-115.81 290.73-253.72 0-42.53-13.14-82.62-36.34-117.86l-30.99 30.99Z"/>
51
+ </svg>`,
52
+ listView:{
53
+ title: function(aip){
54
+ return `
55
+ <joe-title>${aip.name}</joe-title>
56
+ <joe-subtitle>${aip.info}</joe-subtitle><br/>
57
+
58
+ <joe-subtext class="fright"><div><b>${aip.ai_model}</b></div>${aip.prompt_method}</joe-subtext>
59
+ <joe-subtext>${aip.content_items.map(i=>{
60
+ return `<b>${i.itemtype}</b>: ${i.reference}`;
61
+ }).join('')}</joe-subtext>
62
+ ${_joe.schemas.tag.methods.renderTags(aip.tags,'fleft')}
63
+ `;
64
+ },
65
+ listWindowTitle: 'Ai Prompts'
66
+ },
67
+ subsets:function(i){
68
+
69
+ return _joe.Filter.Options.status();
70
+ },
71
+ filters:function(i){
72
+
73
+ return _joe.Filter.Options.tags();
74
+ },
75
+ stripeColor: function(item) {
76
+ //use the stripe color from ai_assistant status object if it has one
77
+ if (item.status){
78
+ var status = _joe.Cache.get(item.status);
79
+ if (status && status.color) {
80
+ return {color:status.color,title:status.name};
81
+ }
82
+ }
83
+ },
84
+ onload:function(dataset){
85
+ //replace the blank instructions field with the populated template field && the blank instrctions format field with the populated template type field
86
+ dataset.map(function(item){
87
+ if(item.template){
88
+ item.instructions = item.template;
89
+ delete item.template;
90
+ }
91
+ if(item.template_type){
92
+ item.instructions_format = item.template_type;
93
+ delete item.template_type;
94
+ }
95
+ });
96
+ // Best-effort: fetch MCP manifest once and cache tool names on this schema
97
+ try{
98
+ if (!schema._mcpToolNames && typeof fetch === 'function') {
99
+ fetch('/.well-known/mcp/manifest.json')
100
+ .then(function(r){ if(!r.ok){ throw new Error('HTTP '+r.status); } return r.json(); })
101
+ .then(function(man){
102
+ try{
103
+ var tools = (man && man.tools) || [];
104
+ schema._mcpToolNames = tools.map(function(t){ return t && t.name; }).filter(Boolean).sort();
105
+ }catch(_e){}
106
+ })
107
+ .catch(function(e){
108
+ console.warn('ai_prompt mcp manifest load failed', e);
109
+ });
110
+ }
111
+ }catch(_e){}
112
+ },
113
+ fields:[
114
+ 'name',
115
+ 'info',
116
+ {name:"prompt_method",placeholder:"name of method to call in plugin", comment:'use executeJOEAiPrompt to use the JOE ui here for all smarts', default:"executeJOEAiPrompt",display:"Prompt Plugin Method",type:'text'},
117
+ {name:'content_items',type:'objectList',
118
+ properties:['itemtype','reference']
119
+ },
120
+ {section_start:'Input'},
121
+ {name: 'functions', type: 'code', display: 'Helper Functions', language:'javascript',comment: `
122
+ <div>AI Prompt Helper Function
123
+
124
+ <h4>Usage:</h4>
125
+ <ul>
126
+ <li>Must export an async function.</li>
127
+ <li>Receives: { instructions, params, ai_prompt } as a single argument.</li>
128
+ <li>Must return a final modified instructions string.</li>
129
+ </ul>
130
+
131
+ <h4>Example:</h4>
132
+ <div class="pad10">
133
+ <pre>
134
+ module.exports = async function({ instructions, params, ai_prompt })
135
+ // Your code here to modify the instructions based off params
136
+ const object = $J.get(params.idToLookup);
137
+
138
+ trackObject(params.idToLookup); // Tell parent we used this
139
+
140
+ return {
141
+ instructions: "Updated system-level instructions string",
142
+ input: "Specific input text (URL, query, etc)"
143
+ };
144
+
145
+ };
146
+ </pre>
147
+ </div>
148
+ </div>`},
149
+ {section_end:'Input'},
150
+ {section_start:'Instruct'},
151
+ {label:'Instructions'},
152
+ "instructions_format",
153
+ "instructions",
154
+ {name:"user_prompt",display:'User Prompt',type:'rendering',comment:'speciific input to the prompt if any, such as campaign intent'},
155
+
156
+ // 'template_type',
157
+ // {extend:'template',specs:{comment:'legacy field, use instructions instead',
158
+ // // comment:`<div>use $\\{this.AI_PROMPT.name\\} $\\{this.CONTENT\\} <br/></div><div>for <b>module</b>: export function(data) that returns an html string.
159
+ // // data.request, data.REPORT,
160
+ // // <br/>shorthand: $J.get(itemid) $J.schema(schemaname)</div>`,
161
+ // language:function(item){
162
+ // switch(item.template_type){
163
+ // case 'module':
164
+ // return 'javascript';
165
+ // break;
166
+ // default:
167
+ // return '';
168
+ // }
169
+
170
+ // }
171
+ // }},
172
+ {section_end:'Instruct'},
173
+
174
+ {section_start:'system',collapsed:true},
175
+ '_id','created','itemtype',
176
+ {section_end:'system'},
177
+ {sidebar_start:'right',collapsed:true},
178
+ {section_start:'workflow'},
179
+ 'status',
180
+ 'tags',
181
+ // {name:'merge_status',comment:'status to add when this data is merged in', allowMultiple:false,type:"objectReference",display:'Merge Status',values:function(){
182
+ // return _joe.getDataset('status');
183
+ // }},
184
+ {section_end:'workflow'},
185
+ {section_start:'openAi',collapsed:true},
186
+ 'ai_model',
187
+ {name:'attachments_mode', type:'select', display:'Attachments Mode', values:['direct','file_search'], default:'direct', comment:'direct = include files in prompt as context; file_search = index files and retrieve relevant chunks'},
188
+ {name:'temperature', type:'number',display:'Temperature', default:.7, step:"0.1",comment:'0-1, 0 is deterministic, 1 is random'},
189
+ {section_end:'openAi'},
190
+
191
+ {section_start:'mcp', display:'MCP Tools', collapsed:true},
192
+ {
193
+ name:'mcp_enabled',
194
+ type:'boolean',
195
+ display:'Enable MCP tools',
196
+ default:false,
197
+ comment:'When true, this prompt may call JOE MCP tools (read-only by default).'
198
+ },
199
+ {
200
+ name:'mcp_toolset',
201
+ type:'select',
202
+ display:'MCP Toolset',
203
+ values:['read-only','minimal','all','custom'],
204
+ default:'read-only',
205
+ rerender:'mcp_selected_tools',
206
+ comment:'Choose which MCP toolset to expose when MCP tools are enabled.'
207
+ },
208
+ {
209
+ name:'mcp_selected_tools',
210
+ type:'select',
211
+ display:'Custom MCP tools (names)',
212
+ isArray:true,
213
+ values:function(aip){
214
+ try{
215
+ var names = (schema && schema._mcpToolNames) || [];
216
+ return names;
217
+ }catch(e){
218
+ return [];
219
+ }
220
+ },
221
+ hidden:function(aip){
222
+ return !aip || aip.mcp_toolset !== 'custom';
223
+ },
224
+ comment:'Multi-select from MCP manifest tool names. Only used when MCP Toolset = custom.'
225
+ },
226
+ {
227
+ name:'mcp_instructions_mode',
228
+ type:'select',
229
+ display:'MCP Instructions Mode',
230
+ values:['auto','full','none'],
231
+ default:'auto',
232
+ comment:'auto = short per-tool instructions from MCP; full = extended; none = do not add MCP instructions.'
233
+ },
234
+ {section_end:'mcp'},
235
+
236
+
237
+ {section_start:'examples',collapsed:true},
238
+ {name:'examples', display:'prompt examples', comment:'runs the prompt to create a response',type:'content', reloadable:true,run:function(item){
239
+ return $J.schema('ai_prompt').methods.listExamples(item);
240
+ } },
241
+ {section_end:'examples'},
242
+
243
+ {sidebar_end:'right'},
244
+ {sidebar_start:'left',collapsed:function(aip){
245
+ return aip.datasets.length != 0;
246
+ }},
247
+ {section_start:'categorization',collapsed:false},
248
+ 'datasets',
249
+ {section_end:'categorization'},
250
+ {sidebar_end:'left'},
251
+ ],
252
+ idprop : "_id",
253
+ sorter:['name','!joeUpdated'],
254
+ methods:{
255
+ buildURL: function(prompt, items = []) {
256
+ const method = prompt.prompt_method || '';
257
+ let url = `/API/plugin/chatgpt/${method}?ai_prompt=${prompt._id}`;
258
+
259
+ if (!Array.isArray(items)) {
260
+ console.warn('Expected items to be an array.');
261
+ return url;
262
+ }
263
+
264
+ if (prompt.content_items) {
265
+ prompt.content_items.forEach(ci => {
266
+ const matchingItem = items.find(it => it.itemtype === ci.itemtype );
267
+ if (matchingItem) {
268
+ url += `&${ci.reference}=${matchingItem._id}`;
269
+ }
270
+ });
271
+ }
272
+
273
+ return url;
274
+ },
275
+ // listItemClickHandler:function(itemid,promptid){
276
+ // var item = $J.get(itemid);
277
+ // var prompt;
278
+ // if(promptid){
279
+ // prompt = $J.get(promptid);
280
+ // }else{
281
+ // prompt = _jco();
282
+ // }
283
+ // window.open(this.buildURL(prompt,[item]));
284
+ // },
285
+ listItemClickHandler: function (itemMap, promptId) {
286
+ const prompt = promptId ? $J.get(promptId,'ai_prompt') : _jco();
287
+ const items = [];
288
+
289
+ for (const [itemtype, id] of Object.entries(itemMap)) {
290
+ const obj = $J.get(id,itemtype);
291
+ if (obj) {
292
+ items.push(obj);
293
+ }
294
+ }
295
+
296
+ window.open(this.buildURL(prompt, items));
297
+ },
298
+ listExamples:function(prompt){
299
+ var examples = [];
300
+ var html ='';
301
+ var ref0;
302
+ var it0;
303
+ if(!prompt.content_items){
304
+ return 'select content items to send with this prompt';
305
+ }
306
+ prompt.content_items.map(function(ci){
307
+ if(_joe.Data[ci.itemtype]){
308
+ examples = examples.concat(_joe.Data[ci.itemtype]);
309
+ it0 = ci.itemtype;
310
+ ref0 = ci.reference;
311
+ }
312
+ })
313
+ var finalExamples = [];
314
+
315
+ while(finalExamples.length < 5 && finalExamples.length < examples.length){
316
+ let ci = examples[Math.floor(Math.random()*examples.length)];
317
+ if(finalExamples.indexOf(ci) == -1){
318
+ finalExamples.push(ci);
319
+ }
320
+ }
321
+ var rep_url;
322
+ var method =
323
+ finalExamples.map(function(ex){
324
+ //rep_url = '/API/plugin/chatgpt'+'?ai_prompt='+prompt._id+'&'+ref0+'='+ex._id;
325
+ html += _joe.renderFieldListItem(ex,
326
+ '<joe-subtext>'+it0+'</joe-subtext>'+
327
+ '<joe-title>${name} ${date}</joe-title>\
328
+ <joe-subtext>${website}</joe-subtext>',
329
+ it0,
330
+ {
331
+ //action:'onclick="window.open(\''+rep_url+'\');"',
332
+ action:`onclick="_joe.schemas.ai_prompt.methods.listItemClickHandler('${ex._id}');"`,
333
+ icon:'ai_prompt'
334
+ }
335
+ );
336
+ })
337
+
338
+ return html;
339
+ },
340
+ listPrompts: function (objectMap) {
341
+ if (!objectMap || typeof objectMap !== 'object') return '';
342
+
343
+ const activeStatuses = _joe.getDataset('status')
344
+ .filter(f => f.datasets.includes('ai_prompt') && f.active)
345
+ .map(f => f._id);
346
+
347
+ const prompts = _joe.getDataset('ai_prompt').filter(prompt =>
348
+ activeStatuses.includes(prompt.status)
349
+ );
350
+
351
+ const context_items = {}
352
+ // Inline helper: reduce objects to { itemtype: _id }
353
+ const extractIds = map => {
354
+ const idMap = {};
355
+ for (const [type, obj] of Object.entries(map)) {
356
+ if (obj && obj._id) idMap[type] = obj._id;
357
+ }
358
+ return idMap;
359
+ };
360
+
361
+ const objectIdMap = extractIds(objectMap);
362
+ const paramStr = JSON.stringify(objectIdMap).replace(/"/g, '&quot;');
363
+
364
+ let html = '';
365
+ const temp = '<joe-title>${name}</joe-title>';
366
+
367
+
368
+ let itemtype = _joe.current.object.itemtype;
369
+ const matchingPrompts = prompts.filter(prompt =>
370
+ (prompt.content_items || []).some(ci => ci.itemtype === itemtype)
371
+ );
372
+
373
+ matchingPrompts.forEach(prompt => {
374
+ html += _joe.renderFieldListItem(prompt, temp, 'ai_prompt', {
375
+ icon: 'ai_prompt',
376
+ action: `onclick="(function(){ _joe.schemas.ai_prompt.methods.listItemClickHandler(${paramStr}, '${prompt._id}'); })()"`
377
+
378
+ //action: `onclick="_joe.schemas.ai_prompt.methods.listItemClickHandler(${paramStr}, "${prompt._id}");"`
379
+ });
380
+ });
381
+
382
+
383
+ return html;
384
+ }
385
+
386
+
387
+ }
388
+ };
389
+
325
390
  module.exports = schema;