@saltcorn/copilot 0.8.1 → 0.8.3
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/actions/generate-page.js +6 -2
- package/actions/generate-tables.js +70 -6
- package/actions/generate-workflow.js +54 -3
- package/agent-skills/pagegen.js +171 -59
- package/agent-skills/registry-editor.js +30 -2
- package/agent-skills/triggergen.js +1 -5
- package/agent-skills/viewgen.js +49 -7
- package/app-constructor/common.js +7 -1
- package/app-constructor/errors.js +749 -61
- package/app-constructor/feedback-action.js +62 -60
- package/app-constructor/feedback.js +1294 -67
- package/app-constructor/fixed-prompts.js +829 -0
- package/app-constructor/phases.js +1485 -0
- package/app-constructor/prompt-generator.js +587 -0
- package/app-constructor/requirements.js +171 -50
- package/app-constructor/research.js +350 -0
- package/app-constructor/run_task.js +234 -73
- package/app-constructor/schema.js +173 -169
- package/app-constructor/tasks.js +96 -537
- package/app-constructor/tools.js +17 -4
- package/app-constructor/view.js +314 -54
- package/builder-gen.js +90 -41
- package/builder-schema.js +6 -0
- package/copilot-as-agent.js +1 -0
- package/index.js +0 -1
- package/js-code-gen.js +1 -0
- package/package.json +1 -1
- package/relation-paths.js +73 -40
- package/standard-prompt.js +1 -0
- package/user-copilot.js +2 -0
- package/workflow-gen.js +1 -0
- package/app-constructor/prompts.js +0 -120
- package/chat-copilot.js +0 -770
package/app-constructor/tools.js
CHANGED
|
@@ -46,12 +46,19 @@ const task_tool = {
|
|
|
46
46
|
type: "array",
|
|
47
47
|
items: {
|
|
48
48
|
type: "object",
|
|
49
|
-
required: [
|
|
49
|
+
required: [
|
|
50
|
+
"name",
|
|
51
|
+
"description",
|
|
52
|
+
"priority",
|
|
53
|
+
"depends_on",
|
|
54
|
+
"task_type",
|
|
55
|
+
],
|
|
50
56
|
additionalProperties: false,
|
|
51
57
|
properties: {
|
|
52
58
|
name: {
|
|
53
59
|
type: "string",
|
|
54
|
-
description:
|
|
60
|
+
description:
|
|
61
|
+
"A short unique name for the task (snake_case). Every other task that depends on this task must use exactly this name in their depends_on array.",
|
|
55
62
|
},
|
|
56
63
|
description: {
|
|
57
64
|
type: "string",
|
|
@@ -65,11 +72,17 @@ const task_tool = {
|
|
|
65
72
|
depends_on: {
|
|
66
73
|
type: "array",
|
|
67
74
|
description:
|
|
68
|
-
"
|
|
75
|
+
"Names of tasks in THIS plan that must complete before this task starts. Every name listed here MUST exactly match the name of another task in this same plan_tasks call. Never reference a task name that is not present in the tasks array.",
|
|
69
76
|
items: {
|
|
70
77
|
type: "string",
|
|
71
78
|
},
|
|
72
79
|
},
|
|
80
|
+
task_type: {
|
|
81
|
+
type: "string",
|
|
82
|
+
enum: ["plugin", "data_model", "feature"],
|
|
83
|
+
description:
|
|
84
|
+
"plugin: specialized — installs a plugin from the Saltcorn plugin store. data_model: specialized — creates or modifies database tables/fields only. feature: broad catch-all — creates views, pages, triggers, workflows, or anything else not covered by the specialized types. Order: plugin tasks first, then data_model, then feature.",
|
|
85
|
+
},
|
|
73
86
|
},
|
|
74
87
|
},
|
|
75
88
|
},
|
|
@@ -78,4 +91,4 @@ const task_tool = {
|
|
|
78
91
|
},
|
|
79
92
|
};
|
|
80
93
|
|
|
81
|
-
module.exports = { requirements_tool,task_tool };
|
|
94
|
+
module.exports = { requirements_tool, task_tool };
|
package/app-constructor/view.js
CHANGED
|
@@ -36,14 +36,20 @@ const {
|
|
|
36
36
|
const { getState } = require("@saltcorn/data/db/state");
|
|
37
37
|
const renderLayout = require("@saltcorn/markup/layout");
|
|
38
38
|
const { viewname } = require("./common");
|
|
39
|
-
const {
|
|
40
|
-
const {
|
|
41
|
-
const {
|
|
42
|
-
|
|
39
|
+
const { showSchema, schema_routes, schemaStaticScript } = require("./schema");
|
|
40
|
+
const { task_routes } = require("./tasks");
|
|
41
|
+
const {
|
|
42
|
+
errorList,
|
|
43
|
+
doCreateErrorFixTask,
|
|
44
|
+
error_routes,
|
|
45
|
+
errTableStaticHtml,
|
|
46
|
+
} = require("./errors");
|
|
47
|
+
const { req_routes, requirementsStaticScript } = require("./requirements");
|
|
43
48
|
const { feedbackList, feedback_routes } = require("./feedback");
|
|
44
|
-
const {
|
|
49
|
+
const { progress_routes } = require("./progress");
|
|
45
50
|
const { runNextTask } = require("./run_task");
|
|
46
|
-
const {
|
|
51
|
+
const { researchPanel, research_routes } = require("./research");
|
|
52
|
+
const { phasesPanel, phasesStaticScript, phase_routes } = require("./phases");
|
|
47
53
|
|
|
48
54
|
const get_state_fields = () => [];
|
|
49
55
|
|
|
@@ -56,37 +62,14 @@ const makeSpecForm = async (req) => {
|
|
|
56
62
|
});
|
|
57
63
|
|
|
58
64
|
return new Form({
|
|
59
|
-
blurb: "
|
|
65
|
+
blurb: "Describe the application you want to build",
|
|
60
66
|
fields: [
|
|
61
67
|
{
|
|
62
|
-
name: "
|
|
63
|
-
label: "
|
|
64
|
-
type: "String",
|
|
65
|
-
fieldview: "textarea",
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: "audience",
|
|
69
|
-
label: "Audience",
|
|
70
|
-
type: "String",
|
|
71
|
-
fieldview: "textarea",
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: "core_features",
|
|
75
|
-
label: "Core features",
|
|
76
|
-
type: "String",
|
|
77
|
-
fieldview: "textarea",
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: "out_of_scope",
|
|
81
|
-
label: "Out of scope",
|
|
82
|
-
type: "String",
|
|
83
|
-
fieldview: "textarea",
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: "visual_style",
|
|
87
|
-
label: "Visual style",
|
|
68
|
+
name: "specification",
|
|
69
|
+
label: "Specification",
|
|
88
70
|
type: "String",
|
|
89
71
|
fieldview: "textarea",
|
|
72
|
+
attributes: { rows: 10 },
|
|
90
73
|
},
|
|
91
74
|
],
|
|
92
75
|
xhrSubmit: true,
|
|
@@ -95,14 +78,95 @@ const makeSpecForm = async (req) => {
|
|
|
95
78
|
});
|
|
96
79
|
};
|
|
97
80
|
|
|
81
|
+
const specDepsModal = `
|
|
82
|
+
<div class="modal fade" id="specDepsModal" tabindex="-1" aria-hidden="true">
|
|
83
|
+
<div class="modal-dialog">
|
|
84
|
+
<div class="modal-content">
|
|
85
|
+
<div class="modal-header">
|
|
86
|
+
<h5 class="modal-title">Specification changed</h5>
|
|
87
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="modal-body">
|
|
90
|
+
<p>The following items were generated from the previous specification
|
|
91
|
+
and may now be outdated. Select any you want to clear:</p>
|
|
92
|
+
<div id="spec-deps-checks"></div>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="modal-footer">
|
|
95
|
+
<button type="button" class="btn btn-secondary" id="specDepsKeepBtn">Save (keep all)</button>
|
|
96
|
+
<button type="button" class="btn btn-primary" id="specDepsSaveBtn">Clear selected & save</button>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>`;
|
|
101
|
+
|
|
102
|
+
const specDepsScript = (vn) =>
|
|
103
|
+
script(
|
|
104
|
+
domReady(`
|
|
105
|
+
const _specVn = ${JSON.stringify(vn)};
|
|
106
|
+
const form = document.querySelector('form[action*="submit_specs"]');
|
|
107
|
+
if (!form) return;
|
|
108
|
+
const btn = form.querySelector('button[onclick*="ajaxSubmitForm"]');
|
|
109
|
+
if (!btn) return;
|
|
110
|
+
|
|
111
|
+
const doSaveSpec = () => {
|
|
112
|
+
const data = {};
|
|
113
|
+
for (const [k, v] of new FormData(form)) data[k] = v;
|
|
114
|
+
view_post(_specVn, 'submit_specs', data);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Remove Saltcorn's inline onclick and replace with our own handler
|
|
118
|
+
btn.removeAttribute('onclick');
|
|
119
|
+
btn.addEventListener('click', (e) => {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
e.stopPropagation();
|
|
122
|
+
view_post(_specVn, 'check_spec_dependencies', {}, (deps) => {
|
|
123
|
+
const items = [];
|
|
124
|
+
if (deps.hasResearch) items.push({ key: 'clearResearch', label: 'Research questions & answers' });
|
|
125
|
+
if (deps.hasRequirements) items.push({ key: 'clearRequirements', label: 'Requirements' });
|
|
126
|
+
if (deps.hasSchema) items.push({ key: 'clearSchema', label: 'Schema' });
|
|
127
|
+
if (deps.hasPhases) items.push({ key: 'clearPhases', label: 'Phases' });
|
|
128
|
+
if (deps.hasTasks) items.push({ key: 'clearTasks', label: 'Tasks' });
|
|
129
|
+
if (!items.length) { doSaveSpec(); return; }
|
|
130
|
+
document.getElementById('spec-deps-checks').innerHTML = items.map((item) =>
|
|
131
|
+
'<div class="form-check">' +
|
|
132
|
+
'<input class="form-check-input" type="checkbox" id="dep_' + item.key +
|
|
133
|
+
'" value="' + item.key + '" checked>' +
|
|
134
|
+
'<label class="form-check-label" for="dep_' + item.key + '">' + item.label + '</label>' +
|
|
135
|
+
'</div>'
|
|
136
|
+
).join('');
|
|
137
|
+
new bootstrap.Modal(document.getElementById('specDepsModal')).show();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
document.getElementById('specDepsSaveBtn').addEventListener('click', () => {
|
|
142
|
+
const modal = bootstrap.Modal.getInstance(document.getElementById('specDepsModal'));
|
|
143
|
+
const clearData = {};
|
|
144
|
+
document.querySelectorAll('#spec-deps-checks input:checked').forEach((cb) => {
|
|
145
|
+
clearData[cb.value] = '1';
|
|
146
|
+
});
|
|
147
|
+
modal.hide();
|
|
148
|
+
if (Object.keys(clearData).length) {
|
|
149
|
+
const formData = {};
|
|
150
|
+
for (const [k, v] of new FormData(form)) formData[k] = v;
|
|
151
|
+
view_post(_specVn, 'clear_and_save_spec', Object.assign(formData, clearData));
|
|
152
|
+
} else {
|
|
153
|
+
doSaveSpec();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
document.getElementById('specDepsKeepBtn').addEventListener('click', () => {
|
|
158
|
+
bootstrap.Modal.getInstance(document.getElementById('specDepsModal')).hide();
|
|
159
|
+
doSaveSpec();
|
|
160
|
+
});
|
|
161
|
+
`)
|
|
162
|
+
);
|
|
163
|
+
|
|
98
164
|
const run = async (table_id, viewname, cfg, state, { req, res }) => {
|
|
99
165
|
const specForm = await makeSpecForm(req);
|
|
100
|
-
const
|
|
101
|
-
const
|
|
166
|
+
const research = await researchPanel(req);
|
|
167
|
+
const phases = await phasesPanel(req);
|
|
102
168
|
const errList = await errorList(req);
|
|
103
169
|
const feedbacks = await feedbackList(req);
|
|
104
|
-
const progress = await progressList(req);
|
|
105
|
-
const taskChart = await makeTaskChart(req);
|
|
106
170
|
const schema = await showSchema(req);
|
|
107
171
|
const layout = {
|
|
108
172
|
type: "tabs",
|
|
@@ -111,26 +175,40 @@ const run = async (table_id, viewname, cfg, state, { req, res }) => {
|
|
|
111
175
|
lazyLoadViews: true,
|
|
112
176
|
titles: [
|
|
113
177
|
"Specification",
|
|
114
|
-
"
|
|
178
|
+
"Research",
|
|
179
|
+
"Phases",
|
|
115
180
|
"Schema",
|
|
116
|
-
"Tasks",
|
|
117
|
-
"Task chart",
|
|
118
|
-
"Progress",
|
|
119
181
|
"Feedback",
|
|
120
182
|
"Errors",
|
|
121
183
|
],
|
|
122
184
|
contents: [
|
|
123
185
|
{
|
|
124
186
|
type: "blank",
|
|
125
|
-
contents: div(
|
|
187
|
+
contents: div(
|
|
188
|
+
{ class: "mt-2" },
|
|
189
|
+
renderForm(specForm, req.csrfToken()),
|
|
190
|
+
specDepsModal,
|
|
191
|
+
specDepsScript(viewname)
|
|
192
|
+
),
|
|
193
|
+
},
|
|
194
|
+
{ type: "blank", contents: research },
|
|
195
|
+
{ type: "blank", contents: div(phasesStaticScript, phases) },
|
|
196
|
+
{
|
|
197
|
+
type: "blank",
|
|
198
|
+
contents: div(
|
|
199
|
+
schemaStaticScript,
|
|
200
|
+
div({ id: "schema-list-area" }, schema)
|
|
201
|
+
),
|
|
126
202
|
},
|
|
127
|
-
{ type: "blank", contents: reqList },
|
|
128
|
-
{ type: "blank", contents: schema },
|
|
129
|
-
{ type: "blank", contents: taskList },
|
|
130
|
-
{ type: "blank", contents: taskChart },
|
|
131
|
-
{ type: "blank", contents: progress },
|
|
132
203
|
{ type: "blank", contents: feedbacks },
|
|
133
|
-
{
|
|
204
|
+
{
|
|
205
|
+
type: "blank",
|
|
206
|
+
contents: div(
|
|
207
|
+
requirementsStaticScript,
|
|
208
|
+
errTableStaticHtml,
|
|
209
|
+
div({ id: "err-list-area" }, errList)
|
|
210
|
+
),
|
|
211
|
+
},
|
|
134
212
|
],
|
|
135
213
|
deeplink: true,
|
|
136
214
|
tabsStyle: "Tabs",
|
|
@@ -144,6 +222,119 @@ const run = async (table_id, viewname, cfg, state, { req, res }) => {
|
|
|
144
222
|
});
|
|
145
223
|
};
|
|
146
224
|
|
|
225
|
+
const check_spec_dependencies = async (
|
|
226
|
+
table_id,
|
|
227
|
+
viewname,
|
|
228
|
+
config,
|
|
229
|
+
body,
|
|
230
|
+
{ req, res }
|
|
231
|
+
) => {
|
|
232
|
+
const hasResearch =
|
|
233
|
+
!!(await MetaData.findOne({
|
|
234
|
+
type: "CopilotConstructMgr",
|
|
235
|
+
name: "research_questions",
|
|
236
|
+
})) ||
|
|
237
|
+
!!(await MetaData.findOne({
|
|
238
|
+
type: "CopilotConstructMgr",
|
|
239
|
+
name: "research_answers",
|
|
240
|
+
}));
|
|
241
|
+
const hasRequirements =
|
|
242
|
+
(
|
|
243
|
+
await MetaData.find({
|
|
244
|
+
type: "CopilotConstructMgr",
|
|
245
|
+
name: "requirement",
|
|
246
|
+
})
|
|
247
|
+
).length > 0;
|
|
248
|
+
const hasSchema = !!(await MetaData.findOne({
|
|
249
|
+
type: "CopilotConstructMgr",
|
|
250
|
+
name: "schema",
|
|
251
|
+
}));
|
|
252
|
+
const hasPhases =
|
|
253
|
+
(
|
|
254
|
+
await MetaData.find({
|
|
255
|
+
type: "CopilotConstructMgr",
|
|
256
|
+
name: "phase",
|
|
257
|
+
})
|
|
258
|
+
).length > 0;
|
|
259
|
+
const hasTasks =
|
|
260
|
+
(
|
|
261
|
+
await MetaData.find({
|
|
262
|
+
type: "CopilotConstructMgr",
|
|
263
|
+
name: "task",
|
|
264
|
+
})
|
|
265
|
+
).length > 0;
|
|
266
|
+
return {
|
|
267
|
+
json: { hasResearch, hasRequirements, hasPhases, hasSchema, hasTasks },
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// Clears selected dependencies, saves the spec, and reloads — all in one round trip
|
|
272
|
+
const clear_and_save_spec = async (
|
|
273
|
+
table_id,
|
|
274
|
+
viewname,
|
|
275
|
+
config,
|
|
276
|
+
body,
|
|
277
|
+
{ req, res }
|
|
278
|
+
) => {
|
|
279
|
+
const {
|
|
280
|
+
_csrf,
|
|
281
|
+
clearResearch,
|
|
282
|
+
clearRequirements,
|
|
283
|
+
clearSchema,
|
|
284
|
+
clearPhases,
|
|
285
|
+
clearTasks,
|
|
286
|
+
...specBody
|
|
287
|
+
} = body;
|
|
288
|
+
if (clearResearch) {
|
|
289
|
+
for (const name of ["research_questions", "research_answers"]) {
|
|
290
|
+
const md = await MetaData.findOne({ type: "CopilotConstructMgr", name });
|
|
291
|
+
if (md) await md.delete();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (clearRequirements) {
|
|
295
|
+
const rs = await MetaData.find({
|
|
296
|
+
type: "CopilotConstructMgr",
|
|
297
|
+
name: "requirement",
|
|
298
|
+
});
|
|
299
|
+
for (const r of rs) await r.delete();
|
|
300
|
+
}
|
|
301
|
+
if (clearSchema) {
|
|
302
|
+
const md = await MetaData.findOne({
|
|
303
|
+
type: "CopilotConstructMgr",
|
|
304
|
+
name: "schema",
|
|
305
|
+
});
|
|
306
|
+
if (md) await md.delete();
|
|
307
|
+
}
|
|
308
|
+
if (clearTasks) {
|
|
309
|
+
const ts = await MetaData.find({
|
|
310
|
+
type: "CopilotConstructMgr",
|
|
311
|
+
name: "task",
|
|
312
|
+
});
|
|
313
|
+
for (const t of ts) await t.delete();
|
|
314
|
+
}
|
|
315
|
+
if (clearPhases) {
|
|
316
|
+
const ps = await MetaData.find({
|
|
317
|
+
type: "CopilotConstructMgr",
|
|
318
|
+
name: "phase",
|
|
319
|
+
});
|
|
320
|
+
for (const ph of ps) await ph.delete();
|
|
321
|
+
}
|
|
322
|
+
const existing = await MetaData.findOne({
|
|
323
|
+
type: "CopilotConstructMgr",
|
|
324
|
+
name: "spec",
|
|
325
|
+
});
|
|
326
|
+
if (existing)
|
|
327
|
+
await db.update("_sc_metadata", { body: specBody }, existing.id);
|
|
328
|
+
else
|
|
329
|
+
await MetaData.create({
|
|
330
|
+
type: "CopilotConstructMgr",
|
|
331
|
+
name: "spec",
|
|
332
|
+
user_id: req.user?.id || undefined,
|
|
333
|
+
body: specBody,
|
|
334
|
+
});
|
|
335
|
+
return { json: { reload_page: true } };
|
|
336
|
+
};
|
|
337
|
+
|
|
147
338
|
const submit_specs = async (table_id, viewname, config, body, { req, res }) => {
|
|
148
339
|
const { _csrf, ...spec } = body;
|
|
149
340
|
const existing = await MetaData.findOne({
|
|
@@ -159,6 +350,42 @@ const submit_specs = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
159
350
|
user_id: req.user?.id || undefined,
|
|
160
351
|
body: spec,
|
|
161
352
|
});
|
|
353
|
+
return {
|
|
354
|
+
json: {
|
|
355
|
+
success: "ok",
|
|
356
|
+
notify: "Specification saved",
|
|
357
|
+
notify_type: "success",
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// Creates fix tasks for application errors recorded while self-healing was off.
|
|
363
|
+
const healPendingErrors = async () => {
|
|
364
|
+
const settings = await MetaData.findOne({
|
|
365
|
+
type: "CopilotConstructMgr",
|
|
366
|
+
name: "settings",
|
|
367
|
+
});
|
|
368
|
+
if (!settings?.body?.error_heal) return;
|
|
369
|
+
const spec = await MetaData.findOne({
|
|
370
|
+
type: "CopilotConstructMgr",
|
|
371
|
+
name: "spec",
|
|
372
|
+
});
|
|
373
|
+
if (!spec) return;
|
|
374
|
+
const errors = await MetaData.find({
|
|
375
|
+
type: "CopilotConstructMgr",
|
|
376
|
+
name: "error",
|
|
377
|
+
});
|
|
378
|
+
for (const err of errors) {
|
|
379
|
+
if (
|
|
380
|
+
err.body.source !== "application" ||
|
|
381
|
+
err.body.fix_task_created ||
|
|
382
|
+
err.body.cannot_fix_reason
|
|
383
|
+
)
|
|
384
|
+
continue;
|
|
385
|
+
doCreateErrorFixTask(err, null).catch((e) =>
|
|
386
|
+
console.error("healPendingErrors failed", e)
|
|
387
|
+
);
|
|
388
|
+
}
|
|
162
389
|
};
|
|
163
390
|
|
|
164
391
|
const virtual_triggers = () => {
|
|
@@ -170,20 +397,49 @@ const virtual_triggers = () => {
|
|
|
170
397
|
type: "CopilotConstructMgr",
|
|
171
398
|
name: "error",
|
|
172
399
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
400
|
+
// Allow a new record once a fix task exists — same error recurring after a fix gets another task.
|
|
401
|
+
const unfixedDuplicate =
|
|
402
|
+
row.stack &&
|
|
403
|
+
existing.find(
|
|
404
|
+
(m) =>
|
|
405
|
+
m.body?.error?.stack === row.stack && !m.body?.fix_task_created
|
|
406
|
+
);
|
|
407
|
+
if (unfixedDuplicate) return;
|
|
408
|
+
|
|
409
|
+
const source = (row.stack || "").includes("/app-constructor/")
|
|
410
|
+
? "constructor"
|
|
411
|
+
: "application";
|
|
412
|
+
|
|
413
|
+
const errorMd = await MetaData.create({
|
|
414
|
+
type: "CopilotConstructMgr",
|
|
415
|
+
name: "error",
|
|
416
|
+
body: { status: "New", error: row, source },
|
|
417
|
+
user_id: null,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if (source === "application") {
|
|
421
|
+
const settings = await MetaData.findOne({
|
|
176
422
|
type: "CopilotConstructMgr",
|
|
177
|
-
name: "
|
|
178
|
-
body: { status: "New", error: row },
|
|
179
|
-
user_id: null,
|
|
423
|
+
name: "settings",
|
|
180
424
|
});
|
|
425
|
+
if (settings?.body?.error_heal) {
|
|
426
|
+
const spec = await MetaData.findOne({
|
|
427
|
+
type: "CopilotConstructMgr",
|
|
428
|
+
name: "spec",
|
|
429
|
+
});
|
|
430
|
+
if (spec)
|
|
431
|
+
doCreateErrorFixTask(errorMd, null).catch((e) =>
|
|
432
|
+
console.error("auto error fix task failed", e)
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
181
436
|
},
|
|
182
437
|
},
|
|
183
438
|
{
|
|
184
439
|
when_trigger: "Often",
|
|
185
440
|
run: async () => {
|
|
186
441
|
await runNextTask();
|
|
442
|
+
await healPendingErrors();
|
|
187
443
|
},
|
|
188
444
|
},
|
|
189
445
|
];
|
|
@@ -198,12 +454,16 @@ module.exports = {
|
|
|
198
454
|
run,
|
|
199
455
|
routes: {
|
|
200
456
|
submit_specs,
|
|
457
|
+
check_spec_dependencies,
|
|
458
|
+
clear_and_save_spec,
|
|
201
459
|
...req_routes,
|
|
460
|
+
...research_routes,
|
|
202
461
|
...task_routes,
|
|
203
462
|
...error_routes,
|
|
204
463
|
...feedback_routes,
|
|
205
464
|
...progress_routes,
|
|
206
465
|
...schema_routes,
|
|
466
|
+
...phase_routes,
|
|
207
467
|
},
|
|
208
468
|
virtual_triggers,
|
|
209
469
|
};
|