@lazyingart/agintiflow 0.20.192 → 0.20.193
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.193",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,6 +10,18 @@ This note records the working publication route for AgInTiFlow so future release
|
|
|
10
10
|
|
|
11
11
|
Latest verified release:
|
|
12
12
|
|
|
13
|
+
- Version: `0.20.192`
|
|
14
|
+
- Commit: `fe571c0`
|
|
15
|
+
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/27003317331`
|
|
16
|
+
- Workflow result: success
|
|
17
|
+
- npm registry check: `npm view @lazyingart/agintiflow version dist-tags.latest --registry=https://registry.npmjs.org` returned `0.20.192`.
|
|
18
|
+
- Temporary install verification: `npm install --prefix <tmp> -g @lazyingart/agintiflow@0.20.192` then `<tmp>/bin/aginti --version` returned `0.20.192`.
|
|
19
|
+
- Installed verification: `npm install -g @lazyingart/agintiflow@0.20.192` then `aginti --version` returned `0.20.192`.
|
|
20
|
+
- Webapp verification: `aginti webapp restart --port 3210` then `curl -fsS http://127.0.0.1:3210/health` returned `version":"0.20.192"` from the global npm package path.
|
|
21
|
+
- Feature verification: `npm run smoke:auth`, `npm run smoke:auxiliary-tools`, `npm run smoke:web-api`, `npm run check`, full `npm test`, and `npm pack --dry-run` passed before release; post-version-bump checks covered `npm run check`, `npm run smoke:auth`, `npm run smoke:auxiliary-tools`, and `npm pack --dry-run`.
|
|
22
|
+
|
|
23
|
+
Previous verified release:
|
|
24
|
+
|
|
13
25
|
- Version: `0.20.190`
|
|
14
26
|
- Commit: `f192f8e`
|
|
15
27
|
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26736284533`
|
|
@@ -162,6 +162,33 @@ try {
|
|
|
162
162
|
interruptedDeepSeekState.messages.at(-1)?.content === "Continue with this new request: /review",
|
|
163
163
|
"interrupted repair dropped the new user request"
|
|
164
164
|
);
|
|
165
|
+
const workspaceToolConfig = {
|
|
166
|
+
commandCwd: workspace,
|
|
167
|
+
allowFileTools: true,
|
|
168
|
+
workspaceWritePolicy: "allow",
|
|
169
|
+
sandboxMode: "host",
|
|
170
|
+
};
|
|
171
|
+
const cdataSvgResult = await executeWorkspaceTool(
|
|
172
|
+
"write_file",
|
|
173
|
+
{
|
|
174
|
+
path: "figures/cdata-wrapped.svg",
|
|
175
|
+
content: "<![CDATA[<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\"></svg>]]>",
|
|
176
|
+
},
|
|
177
|
+
workspaceToolConfig
|
|
178
|
+
);
|
|
179
|
+
assert(cdataSvgResult.ok === false, "CDATA-wrapped SVG write should be marked not ok");
|
|
180
|
+
assert(cdataSvgResult.artifactValidation?.kind === "svg", "SVG validation result missing");
|
|
181
|
+
assert(/CDATA/i.test(cdataSvgResult.artifactValidation.errors.join(" ")), "CDATA SVG validation did not explain the wrapper problem");
|
|
182
|
+
const validSvgResult = await executeWorkspaceTool(
|
|
183
|
+
"write_file",
|
|
184
|
+
{
|
|
185
|
+
path: "figures/valid.svg",
|
|
186
|
+
content: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\"><rect width=\"10\" height=\"10\" /></svg>",
|
|
187
|
+
},
|
|
188
|
+
workspaceToolConfig
|
|
189
|
+
);
|
|
190
|
+
assert(validSvgResult.ok === true, "valid standalone SVG write should pass");
|
|
191
|
+
assert(validSvgResult.artifactValidation?.ok === true, "valid SVG validation did not pass");
|
|
165
192
|
const blockedBatchResult = {
|
|
166
193
|
ok: false,
|
|
167
194
|
blocked: true,
|
|
@@ -1237,6 +1264,8 @@ try {
|
|
|
1237
1264
|
"resume_session_write",
|
|
1238
1265
|
"resume_runtime_time_context",
|
|
1239
1266
|
"virtual_workspace_path",
|
|
1267
|
+
"svg_cdata_validation_failure",
|
|
1268
|
+
"svg_standalone_validation_pass",
|
|
1240
1269
|
"apply_patch",
|
|
1241
1270
|
"multi_file_patch",
|
|
1242
1271
|
"unified_patch",
|
package/src/model-client.js
CHANGED
|
@@ -1535,7 +1535,7 @@ export async function requestNextStep(client, config, messages) {
|
|
|
1535
1535
|
function: {
|
|
1536
1536
|
name: "write_file",
|
|
1537
1537
|
description:
|
|
1538
|
-
"Create or overwrite a small UTF-8 workspace file. Use mode=create with a descriptive non-conflicting filename for newly generated standalone content. Use mode=overwrite only when the user explicitly asks to replace/update that file, or after reading the file and the task is clearly to modify it. Secret paths, .git, node_modules writes, and outside-workspace paths are blocked. The runtime records before/after hashes and a compact diff.",
|
|
1538
|
+
"Create or overwrite a small UTF-8 workspace file. Use mode=create with a descriptive non-conflicting filename for newly generated standalone content. Use mode=overwrite only when the user explicitly asks to replace/update that file, or after reading the file and the task is clearly to modify it. Secret paths, .git, node_modules writes, and outside-workspace paths are blocked. The runtime records before/after hashes and a compact diff. For .svg files, the runtime reports deterministic XML/SVG validation; if that validation is not ok, repair the file before claiming it is valid.",
|
|
1539
1539
|
parameters: {
|
|
1540
1540
|
type: "object",
|
|
1541
1541
|
properties: {
|
package/src/workspace-tools.js
CHANGED
|
@@ -99,6 +99,95 @@ function compactPreview(value, limit = 180) {
|
|
|
99
99
|
return text.length <= limit ? text : `${text.slice(0, limit)}...`;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function firstXmlElementName(source = "") {
|
|
103
|
+
let text = String(source || "").replace(/^\uFEFF/, "").trimStart();
|
|
104
|
+
let changed = true;
|
|
105
|
+
while (changed) {
|
|
106
|
+
changed = false;
|
|
107
|
+
const xmlDeclaration = text.match(/^<\?xml\b[^>]*\?>\s*/i);
|
|
108
|
+
if (xmlDeclaration) {
|
|
109
|
+
text = text.slice(xmlDeclaration[0].length).trimStart();
|
|
110
|
+
changed = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const comment = text.match(/^<!--[\s\S]*?-->\s*/);
|
|
114
|
+
if (comment) {
|
|
115
|
+
text = text.slice(comment[0].length).trimStart();
|
|
116
|
+
changed = true;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const doctype = text.match(/^<!DOCTYPE\b[^>]*>\s*/i);
|
|
120
|
+
if (doctype) {
|
|
121
|
+
text = text.slice(doctype[0].length).trimStart();
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return text.match(/^<([A-Za-z_][\w:.-]*)\b/)?.[1] || "";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function validateXmlTagStack(source = "") {
|
|
129
|
+
const stack = [];
|
|
130
|
+
const errors = [];
|
|
131
|
+
const tagPattern = /<[^>]+>/g;
|
|
132
|
+
for (const match of String(source || "").matchAll(tagPattern)) {
|
|
133
|
+
const tag = match[0];
|
|
134
|
+
if (/^<\?/.test(tag) || /^<!--/.test(tag) || /^<!DOCTYPE\b/i.test(tag) || /^<!\[CDATA\[/i.test(tag)) continue;
|
|
135
|
+
const end = tag.match(/^<\/\s*([A-Za-z_][\w:.-]*)\s*>$/);
|
|
136
|
+
if (end) {
|
|
137
|
+
const expected = stack.pop();
|
|
138
|
+
if (expected !== end[1]) {
|
|
139
|
+
errors.push(`Mismatched closing tag </${end[1]}>${expected ? `, expected </${expected}>` : " with no open tag"}.`);
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const start = tag.match(/^<\s*([A-Za-z_][\w:.-]*)\b/);
|
|
144
|
+
if (!start) {
|
|
145
|
+
errors.push(`Malformed XML tag near: ${compactPreview(tag, 80)}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (/\/\s*>$/.test(tag)) continue;
|
|
149
|
+
stack.push(start[1]);
|
|
150
|
+
}
|
|
151
|
+
while (stack.length) errors.push(`Unclosed XML tag <${stack.pop()}>.`);
|
|
152
|
+
return errors;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateSvgArtifact(relativePath, content) {
|
|
156
|
+
if (path.extname(String(relativePath || "")).toLowerCase() !== ".svg") return null;
|
|
157
|
+
const source = String(content ?? "");
|
|
158
|
+
const trimmed = source.replace(/^\uFEFF/, "").trim();
|
|
159
|
+
const errors = [];
|
|
160
|
+
const warnings = [];
|
|
161
|
+
if (!trimmed) errors.push("SVG file is empty.");
|
|
162
|
+
if (/^<!\[CDATA\[[\s\S]*\]\]>\s*$/i.test(trimmed)) {
|
|
163
|
+
errors.push("SVG is wrapped in a top-level CDATA section; a standalone .svg must start with <svg or an XML declaration.");
|
|
164
|
+
}
|
|
165
|
+
if (/^<!\[CDATA\[/i.test(trimmed) && !/^<!\[CDATA\[[\s\S]*\]\]>\s*$/i.test(trimmed)) {
|
|
166
|
+
errors.push("SVG starts with CDATA instead of an XML/SVG root element.");
|
|
167
|
+
}
|
|
168
|
+
const root = firstXmlElementName(trimmed);
|
|
169
|
+
if (!root) {
|
|
170
|
+
errors.push("No XML root element found.");
|
|
171
|
+
} else if (root !== "svg") {
|
|
172
|
+
errors.push(`Root element is <${root}>; expected <svg>.`);
|
|
173
|
+
}
|
|
174
|
+
const cdataOpen = (trimmed.match(/<!\[CDATA\[/gi) || []).length;
|
|
175
|
+
const cdataClose = (trimmed.match(/\]\]>/g) || []).length;
|
|
176
|
+
if (cdataOpen !== cdataClose) errors.push("CDATA open/close markers are unbalanced.");
|
|
177
|
+
errors.push(...validateXmlTagStack(trimmed));
|
|
178
|
+
if (/<text\b/i.test(trimmed) && !/(viewBox|width|height)=/i.test(trimmed)) {
|
|
179
|
+
warnings.push("SVG contains text but no obvious sizing/viewBox metadata; render-preview before claiming visual fit.");
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
kind: "svg",
|
|
183
|
+
ok: errors.length === 0,
|
|
184
|
+
parser: "builtin-svg-xml-guard",
|
|
185
|
+
root: root || "",
|
|
186
|
+
errors,
|
|
187
|
+
warnings,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
102
191
|
function countOccurrences(text, search) {
|
|
103
192
|
if (!search) return 0;
|
|
104
193
|
let count = 0;
|
|
@@ -732,6 +821,7 @@ async function writeChange(target, nextContent, action, details = {}) {
|
|
|
732
821
|
const afterBuffer = await fs.readFile(target.absolutePath);
|
|
733
822
|
const afterText = afterBuffer.toString("utf8");
|
|
734
823
|
const afterHash = hashBuffer(afterBuffer);
|
|
824
|
+
const artifactValidation = validateSvgArtifact(target.relativePath, afterText);
|
|
735
825
|
|
|
736
826
|
return {
|
|
737
827
|
ok: true,
|
|
@@ -744,6 +834,7 @@ async function writeChange(target, nextContent, action, details = {}) {
|
|
|
744
834
|
created: !existed,
|
|
745
835
|
diff: compactDiff(target.relativePath, beforeText, afterText),
|
|
746
836
|
contentSha256: hashText(content),
|
|
837
|
+
...(artifactValidation ? { artifactValidation } : {}),
|
|
747
838
|
...details,
|
|
748
839
|
};
|
|
749
840
|
}
|
|
@@ -784,10 +875,11 @@ async function writeFile(config, args) {
|
|
|
784
875
|
mode,
|
|
785
876
|
});
|
|
786
877
|
return {
|
|
787
|
-
ok: true,
|
|
878
|
+
ok: change.artifactValidation ? change.artifactValidation.ok : true,
|
|
788
879
|
toolName: "write_file",
|
|
789
880
|
path: target.relativePath,
|
|
790
881
|
change,
|
|
882
|
+
...(change.artifactValidation ? { artifactValidation: change.artifactValidation } : {}),
|
|
791
883
|
};
|
|
792
884
|
}
|
|
793
885
|
|
|
@@ -1060,11 +1152,12 @@ async function applyPatchDocument(config, args) {
|
|
|
1060
1152
|
}
|
|
1061
1153
|
|
|
1062
1154
|
return {
|
|
1063
|
-
ok:
|
|
1155
|
+
ok: !changes.some((change) => change.artifactValidation && !change.artifactValidation.ok),
|
|
1064
1156
|
toolName: "apply_patch",
|
|
1065
1157
|
path: changes.length === 1 ? changes[0].path : "",
|
|
1066
1158
|
changes,
|
|
1067
1159
|
change: changes[0],
|
|
1160
|
+
artifactValidations: changes.map((change) => change.artifactValidation).filter(Boolean),
|
|
1068
1161
|
summary: `${changes.length} file change(s) applied`,
|
|
1069
1162
|
};
|
|
1070
1163
|
}
|