@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/agent-skills/viewgen.js
CHANGED
|
@@ -23,6 +23,21 @@ const {
|
|
|
23
23
|
getRelationPathsForPairs,
|
|
24
24
|
} = require("../relation-paths");
|
|
25
25
|
|
|
26
|
+
const collectViewLinks = (segment, out = []) => {
|
|
27
|
+
if (!segment || typeof segment !== "object") return out;
|
|
28
|
+
if (Array.isArray(segment)) {
|
|
29
|
+
segment.forEach((s) => collectViewLinks(s, out));
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
if (segment.type === "view_link" && segment.view) out.push(segment);
|
|
33
|
+
if (segment.above) collectViewLinks(segment.above, out);
|
|
34
|
+
if (segment.besides) collectViewLinks(segment.besides, out);
|
|
35
|
+
if (segment.contents) collectViewLinks(segment.contents, out);
|
|
36
|
+
if (Array.isArray(segment.tabs))
|
|
37
|
+
segment.tabs.forEach((t) => collectViewLinks(t?.contents, out));
|
|
38
|
+
return out;
|
|
39
|
+
};
|
|
40
|
+
|
|
26
41
|
const collectLayoutFieldNames = (segment, out = new Set()) => {
|
|
27
42
|
if (!segment || typeof segment !== "object") return out;
|
|
28
43
|
if (Array.isArray(segment)) {
|
|
@@ -129,7 +144,8 @@ class GenerateViewSkill {
|
|
|
129
144
|
`(3) Write out the complete updated configuration JSON in full — every key from the existing config must be present, with only your targeted changes merged in.\n` +
|
|
130
145
|
`(4) Call apply_view_config with that complete object. NEVER call apply_view_config before step (3) is finished. NEVER call it with only the name or a partial object — the configuration field is mandatory and must be the full merged result from step (3). Calling apply_view_config without a complete configuration is an error.\n\n` +
|
|
131
146
|
`**Generating a new view that contains view_links or embedded views:**\n` +
|
|
132
|
-
`
|
|
147
|
+
`If the task or prompt mentions a viewlink, a link to another view, or a button that opens another view from a list row, that view_link column is REQUIRED — do not omit it. ` +
|
|
148
|
+
`You MUST call get_relation_paths with all source_table/target_view pairs before constructing the layout. Never skip this step when view_links are needed.\n\n` +
|
|
133
149
|
`**Embedded view segment format (for Show layouts):**\n` +
|
|
134
150
|
` { "type": "view", "view": "<viewName>", "name": "<viewName>", "relation": "<from get_relation_paths>" }\n` +
|
|
135
151
|
`Do NOT use blank text segments as placeholders — always use a real view segment with a relation string from get_relation_paths.\n\n` +
|
|
@@ -161,11 +177,16 @@ class GenerateViewSkill {
|
|
|
161
177
|
error: `View "${name}" already exists. Use get_view_config and apply_view_config to update it.`,
|
|
162
178
|
};
|
|
163
179
|
const tableRow = table ? Table.findOne({ name: table }) : null;
|
|
164
|
-
const roleName =
|
|
180
|
+
const roleName =
|
|
181
|
+
typeof min_role === "number" ? null : min_role || "public";
|
|
165
182
|
const resolvedRole =
|
|
166
183
|
typeof min_role === "number"
|
|
167
184
|
? min_role
|
|
168
|
-
: (
|
|
185
|
+
: (
|
|
186
|
+
(getState().roles || []).find((r) => r.role === roleName) || {
|
|
187
|
+
id: 100,
|
|
188
|
+
}
|
|
189
|
+
).id;
|
|
169
190
|
await View.create({
|
|
170
191
|
name,
|
|
171
192
|
viewtemplate: viewpattern,
|
|
@@ -308,6 +329,21 @@ class GenerateViewSkill {
|
|
|
308
329
|
});
|
|
309
330
|
if (baseCfg?.columns) wfctx.columns = baseCfg.columns;
|
|
310
331
|
}
|
|
332
|
+
if (viewpattern === "List" && wfctx.layout) {
|
|
333
|
+
// initial_config_all_fields never generates ViewLink columns — inject them from layout
|
|
334
|
+
const viewLinks = collectViewLinks(wfctx.layout);
|
|
335
|
+
const viewLinkColumns = viewLinks.map((seg) => ({
|
|
336
|
+
type: "ViewLink",
|
|
337
|
+
view: seg.view,
|
|
338
|
+
block: seg.block || false,
|
|
339
|
+
label: seg.view_label || "",
|
|
340
|
+
minRole: seg.minRole || 100,
|
|
341
|
+
...(seg.relation ? { relation: seg.relation } : {}),
|
|
342
|
+
isFormula: seg.isFormula || {},
|
|
343
|
+
}));
|
|
344
|
+
if (viewLinkColumns.length > 0)
|
|
345
|
+
wfctx.columns = [...(wfctx.columns || []), ...viewLinkColumns];
|
|
346
|
+
}
|
|
311
347
|
if (viewpattern === "Edit" && table) {
|
|
312
348
|
const layoutFieldNames = collectLayoutFieldNames(wfctx.layout);
|
|
313
349
|
const fields = table.fields || [];
|
|
@@ -331,7 +367,10 @@ class GenerateViewSkill {
|
|
|
331
367
|
}
|
|
332
368
|
}
|
|
333
369
|
if (usersFkColumnsToAdd.length > 0)
|
|
334
|
-
wfctx.columns = [
|
|
370
|
+
wfctx.columns = [
|
|
371
|
+
...(wfctx.columns || []),
|
|
372
|
+
...usersFkColumnsToAdd,
|
|
373
|
+
];
|
|
335
374
|
if (Object.keys(fixed).length > 0) wfctx.fixed = fixed;
|
|
336
375
|
wfctx.destination_type = "Back to referer";
|
|
337
376
|
}
|
|
@@ -398,12 +437,15 @@ class GenerateViewSkill {
|
|
|
398
437
|
for (const field of form.fields) {
|
|
399
438
|
if (prefilledFields.has(field.name)) continue;
|
|
400
439
|
//TODO showIf
|
|
440
|
+
const isShowif = field.name.endsWith("_showif");
|
|
401
441
|
properties[field.name] = {
|
|
402
442
|
description:
|
|
403
443
|
field.copilot_description ||
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
444
|
+
(isShowif
|
|
445
|
+
? `${field.label}. The correct default is an empty string — leave it blank to always show this element. Only provide a JavaScript expression if the task description explicitly states that this element should be conditionally hidden based on a URL state variable or the current user. Never invent field names or copy examples.`
|
|
446
|
+
: `${field.label}.${
|
|
447
|
+
field.sublabel ? ` ${field.sublabel}` : ""
|
|
448
|
+
}`),
|
|
407
449
|
...fieldProperties(field),
|
|
408
450
|
};
|
|
409
451
|
if (!properties[field.name].type) {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
const viewname = "Saltcorn AppConstructor (experimental)";
|
|
2
2
|
|
|
3
|
+
const TaskType = Object.freeze({
|
|
4
|
+
PLUGIN: "plugin",
|
|
5
|
+
DATA_MODEL: "data_model",
|
|
6
|
+
FEATURE: "feature",
|
|
7
|
+
});
|
|
8
|
+
|
|
3
9
|
const tool_choice = (tool_name) => ({
|
|
4
10
|
tool_choice: {
|
|
5
11
|
type: "function",
|
|
@@ -9,4 +15,4 @@ const tool_choice = (tool_name) => ({
|
|
|
9
15
|
},
|
|
10
16
|
});
|
|
11
17
|
|
|
12
|
-
module.exports = { viewname, tool_choice };
|
|
18
|
+
module.exports = { viewname, tool_choice, TaskType };
|