@lazyingart/agintiflow 0.20.234 → 0.20.236
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.236",
|
|
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",
|
|
@@ -1074,6 +1074,22 @@ try {
|
|
|
1074
1074
|
documentBuildSequencePolicy.category === "general-shell",
|
|
1075
1075
|
"document build sequence should remain broad trusted shell, not destructive"
|
|
1076
1076
|
);
|
|
1077
|
+
const documentIntegrityBuildPolicy = evaluateCommandPolicy(
|
|
1078
|
+
"set -e; mkdir -p output .verification; echo '== source hashes BEFORE build =='; sha256sum README.md PROJECT_NOTES.md source/budget.csv source/meeting-notes.txt source/style-notes.md | tee .verification/src-before.sha256; echo '== chmod + build =='; chmod +x build.sh scripts/*.py; ./build.sh; echo '== source hashes AFTER build (must match BEFORE) =='; sha256sum README.md PROJECT_NOTES.md source/budget.csv source/meeting-notes.txt source/style-notes.md | tee .verification/src-after.sha256; diff .verification/src-before.sha256 .verification/src-after.sha256 && echo 'SOURCE FILES UNCHANGED (byte-for-byte preserved)'",
|
|
1079
|
+
dockerWorkspacePolicy
|
|
1080
|
+
);
|
|
1081
|
+
assert(
|
|
1082
|
+
documentIntegrityBuildPolicy.allowed,
|
|
1083
|
+
"document integrity build with bounded workspace tee targets should be allowed in trusted Docker mode"
|
|
1084
|
+
);
|
|
1085
|
+
assert(
|
|
1086
|
+
documentIntegrityBuildPolicy.category === "general-shell",
|
|
1087
|
+
"document integrity build should remain broad trusted shell, not destructive"
|
|
1088
|
+
);
|
|
1089
|
+
const externalTeePolicy = evaluateCommandPolicy("tee /etc/aginti-test", dockerWorkspacePolicy);
|
|
1090
|
+
assert(!externalTeePolicy.allowed, "tee outside the workspace should remain blocked");
|
|
1091
|
+
const globTeePolicy = evaluateCommandPolicy("tee reports/*.txt", dockerWorkspacePolicy);
|
|
1092
|
+
assert(!globTeePolicy.allowed, "tee wildcard targets should remain blocked");
|
|
1077
1093
|
const hostGlobChmodPolicy = evaluateCommandPolicy("chmod +x scripts/*.py", hostWorkspacePolicy);
|
|
1078
1094
|
assert(!hostGlobChmodPolicy.allowed, "host workspace chmod globs should require explicit trusted host access");
|
|
1079
1095
|
const recursiveChmodPolicy = evaluateCommandPolicy("chmod -R +x scripts", dockerWorkspacePolicy);
|
|
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
evaluateCurrentStateText,
|
|
5
|
+
evaluateDocumentConsistency,
|
|
5
6
|
evaluatePdfPageBalance,
|
|
6
7
|
extractSupersededLiterals,
|
|
7
8
|
} from "../src/document-artifact-quality.js";
|
|
@@ -42,6 +43,41 @@ const currentDocument = evaluateCurrentStateText({
|
|
|
42
43
|
});
|
|
43
44
|
assert.equal(currentDocument.ok, true, "authoritative current-state prose was rejected");
|
|
44
45
|
|
|
46
|
+
const supersedesHistory = evaluateCurrentStateText({
|
|
47
|
+
sourceText: source,
|
|
48
|
+
outputText: "This document supersedes earlier planning notes. The current owner is Mei.",
|
|
49
|
+
currentStateRequired: true,
|
|
50
|
+
});
|
|
51
|
+
assert.equal(supersedesHistory.ok, false, "present-tense supersedes history was accepted");
|
|
52
|
+
assert(
|
|
53
|
+
supersedesHistory.defects.some((item) => item.code === "historical-transition-prose"),
|
|
54
|
+
"present-tense supersedes did not produce a historical transition defect"
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const inconsistentActions = evaluateDocumentConsistency([
|
|
58
|
+
"Three open actions remain.",
|
|
59
|
+
"Remaining Actions",
|
|
60
|
+
"Place filter order 28 August 2026",
|
|
61
|
+
"Provide wiring diagram 2 September 2026",
|
|
62
|
+
"Complete safety review 5 September 2026",
|
|
63
|
+
"Freeze checklist 10 September 2026",
|
|
64
|
+
"Budget",
|
|
65
|
+
].join("\n"));
|
|
66
|
+
assert.equal(inconsistentActions.ok, false, "declared action count contradicted by the action section was accepted");
|
|
67
|
+
assert.equal(inconsistentActions.actionSectionItemCount, 4);
|
|
68
|
+
assert.equal(inconsistentActions.defects[0]?.code, "action-count-inconsistency");
|
|
69
|
+
|
|
70
|
+
const consistentActions = evaluateDocumentConsistency([
|
|
71
|
+
"Four open actions remain.",
|
|
72
|
+
"Remaining Actions",
|
|
73
|
+
"Place filter order 28 August 2026",
|
|
74
|
+
"Provide wiring diagram 2 September 2026",
|
|
75
|
+
"Complete safety review 5 September 2026",
|
|
76
|
+
"Freeze checklist 10 September 2026",
|
|
77
|
+
"Budget",
|
|
78
|
+
].join("\n"));
|
|
79
|
+
assert.equal(consistentActions.ok, true, "matching declared and section action counts were rejected");
|
|
80
|
+
|
|
45
81
|
function page(
|
|
46
82
|
words,
|
|
47
83
|
{ height = 842, startY = 60, endY = 700, heading = "Section", wordHeight = 10 } = {}
|
package/src/command-policy.js
CHANGED
|
@@ -733,7 +733,7 @@ function classifyBackgroundShell(normalized = "") {
|
|
|
733
733
|
|
|
734
734
|
const SAFE_WORKSPACE_WRITE_PATTERNS = [/^mkdir\s+-p\s+[-\w./]+$/];
|
|
735
735
|
const SAFE_CHMOD_MODE_PATTERN = /^[-+=,rwxugoXst0-7]+$/;
|
|
736
|
-
const
|
|
736
|
+
const SAFE_WORKSPACE_TARGET_LIMIT = 64;
|
|
737
737
|
const SAFE_ENV_ASSIGNMENT_NAMES = new Set(["ANDROID_HOME", "ANDROID_SDK_ROOT", "JAVA_HOME", "GRADLE_USER_HOME", "PATH"]);
|
|
738
738
|
const SAFE_ENV_VALUE_PATTERN = /^[-\w./:@+,%]+$/;
|
|
739
739
|
|
|
@@ -1010,7 +1010,7 @@ function classifyWorkspacePermissionChange(normalized = "") {
|
|
|
1010
1010
|
if (tokens[index] !== "chmod") return null;
|
|
1011
1011
|
const mode = String(tokens[index + 1] || "");
|
|
1012
1012
|
const targets = tokens.slice(index + 2);
|
|
1013
|
-
if (!SAFE_CHMOD_MODE_PATTERN.test(mode) || !targets.length || targets.length >
|
|
1013
|
+
if (!SAFE_CHMOD_MODE_PATTERN.test(mode) || !targets.length || targets.length > SAFE_WORKSPACE_TARGET_LIMIT) {
|
|
1014
1014
|
return null;
|
|
1015
1015
|
}
|
|
1016
1016
|
const unsafeTarget = targets.find((target) => !isSafeWorkspaceChmodTarget(target));
|
|
@@ -1030,6 +1030,33 @@ function classifyWorkspacePermissionChange(normalized = "") {
|
|
|
1030
1030
|
};
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
|
+
function classifyWorkspaceTee(normalized = "") {
|
|
1034
|
+
if (hasActiveShellExpansion(normalized)) return null;
|
|
1035
|
+
const tokens = tokenizeShellWords(normalized);
|
|
1036
|
+
if (tokens[0] !== "tee") return null;
|
|
1037
|
+
let index = 1;
|
|
1038
|
+
if (["-a", "--append"].includes(tokens[index])) index += 1;
|
|
1039
|
+
if (tokens[index] === "--") index += 1;
|
|
1040
|
+
const targets = tokens.slice(index);
|
|
1041
|
+
if (!targets.length || targets.length > SAFE_WORKSPACE_TARGET_LIMIT) return null;
|
|
1042
|
+
const unsafeTarget = targets.find(
|
|
1043
|
+
(target) => target.includes("*") || !isSafeWorkspaceChmodTarget(target)
|
|
1044
|
+
);
|
|
1045
|
+
if (unsafeTarget) {
|
|
1046
|
+
return {
|
|
1047
|
+
category: "blocked",
|
|
1048
|
+
reason: `tee target must be a bounded literal workspace-relative path: ${unsafeTarget}`,
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
return {
|
|
1052
|
+
category: "workspace-write",
|
|
1053
|
+
needsNetwork: false,
|
|
1054
|
+
writesWorkspace: true,
|
|
1055
|
+
virtualWorkspacePath: targets.some((target) => target.startsWith("/workspace/")),
|
|
1056
|
+
reason: `Command writes standard input to ${targets.length} bounded workspace target${targets.length === 1 ? "" : "s"}.`,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1033
1060
|
function isInsideDirectory(root, candidate) {
|
|
1034
1061
|
const relative = path.relative(root, candidate);
|
|
1035
1062
|
return relative === "" || (relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
@@ -1427,6 +1454,8 @@ function classifySimpleCommand(normalized) {
|
|
|
1427
1454
|
}
|
|
1428
1455
|
const permissionChangeClassification = classifyWorkspacePermissionChange(normalized);
|
|
1429
1456
|
if (permissionChangeClassification) return permissionChangeClassification;
|
|
1457
|
+
const teeClassification = classifyWorkspaceTee(normalized);
|
|
1458
|
+
if (teeClassification) return teeClassification;
|
|
1430
1459
|
const gitCloneClassification = classifyGitClone(normalized);
|
|
1431
1460
|
if (gitCloneClassification) return gitCloneClassification;
|
|
1432
1461
|
const envExportClassification = classifySafeEnvExport(normalized);
|
|
@@ -28,8 +28,24 @@ const EXCLUDED_DIRECTORY_NAMES = new Set([
|
|
|
28
28
|
const INTENTIONAL_SPARSE_PAGE_PATTERN =
|
|
29
29
|
/^(?:appendix|approval|approvals|acknowledgements?|back cover|contact|notes|references|sign[- ]?off|signatures?)\b/i;
|
|
30
30
|
const HISTORICAL_TRANSITION_PATTERN =
|
|
31
|
-
/\b(?:formerly|no longer|previously|replac(?:ed|ing)|
|
|
31
|
+
/\b(?:formerly|no longer|previously|replac(?:ed|ing)|supersed(?:e|ed|es|ing)|used to be)\b/i;
|
|
32
32
|
const MIN_READABLE_MEDIAN_WORD_HEIGHT_PT = 8.8;
|
|
33
|
+
const COUNT_WORDS = new Map([
|
|
34
|
+
["zero", 0], ["one", 1], ["two", 2], ["three", 3], ["four", 4], ["five", 5],
|
|
35
|
+
["six", 6], ["seven", 7], ["eight", 8], ["nine", 9], ["ten", 10],
|
|
36
|
+
["eleven", 11], ["twelve", 12], ["thirteen", 13], ["fourteen", 14], ["fifteen", 15],
|
|
37
|
+
["sixteen", 16], ["seventeen", 17], ["eighteen", 18], ["nineteen", 19], ["twenty", 20],
|
|
38
|
+
]);
|
|
39
|
+
const ACTION_COUNT_PATTERN = new RegExp(
|
|
40
|
+
`\\b(\\d+|${[...COUNT_WORDS.keys()].join("|")})\\s+(?:open|remaining|outstanding|pending)\\s+(?:action items?|actions?|tasks?|items?)\\b`,
|
|
41
|
+
"gi"
|
|
42
|
+
);
|
|
43
|
+
const ACTION_SECTION_HEADING_PATTERN =
|
|
44
|
+
/^\s*(?:remaining|open|outstanding|pending)\s+(?:action items?|actions?|tasks?|items?|next steps)\s*$/i;
|
|
45
|
+
const DOCUMENT_SECTION_HEADING_PATTERN =
|
|
46
|
+
/^\s*(?:appendix|budget|current decisions?|executive summary|notes|references|risks?(?: and mitigations)?|summary)\s*$/i;
|
|
47
|
+
const HUMAN_DATE_PATTERN =
|
|
48
|
+
/\b(?:\d{4}-\d{2}-\d{2}|\d{1,2}\s+(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:tember)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)(?:\s+\d{4})?|(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:tember)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\s+\d{1,2}(?:,\s*\d{4})?)\b/gi;
|
|
33
49
|
|
|
34
50
|
function portablePath(value = "") {
|
|
35
51
|
return String(value || "").replace(/\\/g, "/");
|
|
@@ -248,6 +264,47 @@ export function evaluateCurrentStateText({ sourceText = "", outputText = "", cur
|
|
|
248
264
|
return { ok: defects.length === 0, defects, supersededLiterals, presentSupersededLiterals };
|
|
249
265
|
}
|
|
250
266
|
|
|
267
|
+
function parsedCount(value = "") {
|
|
268
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
269
|
+
if (/^\d+$/.test(normalized)) return Number.parseInt(normalized, 10);
|
|
270
|
+
return COUNT_WORDS.get(normalized);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function actionSectionItemCount(outputText = "") {
|
|
274
|
+
const lines = String(outputText || "").split(/\r?\n/);
|
|
275
|
+
const headingIndex = lines.findIndex((line) => ACTION_SECTION_HEADING_PATTERN.test(line));
|
|
276
|
+
if (headingIndex < 0) return 0;
|
|
277
|
+
const section = [];
|
|
278
|
+
for (const line of lines.slice(headingIndex + 1)) {
|
|
279
|
+
if (DOCUMENT_SECTION_HEADING_PATTERN.test(line)) break;
|
|
280
|
+
section.push(line);
|
|
281
|
+
}
|
|
282
|
+
const sectionText = section.join("\n");
|
|
283
|
+
const dates = new Set(
|
|
284
|
+
[...sectionText.matchAll(HUMAN_DATE_PATTERN)].map((match) => normalizedComparableText(match[0]))
|
|
285
|
+
);
|
|
286
|
+
const bullets = section.filter((line) => /^\s*(?:[-*•]|\d+[.)])\s+\S/.test(line)).length;
|
|
287
|
+
return Math.max(dates.size, bullets);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function evaluateDocumentConsistency(outputText = "") {
|
|
291
|
+
const defects = [];
|
|
292
|
+
const sectionCount = actionSectionItemCount(outputText);
|
|
293
|
+
if (sectionCount > 0) {
|
|
294
|
+
for (const match of String(outputText || "").matchAll(ACTION_COUNT_PATTERN)) {
|
|
295
|
+
const declaredCount = parsedCount(match[1]);
|
|
296
|
+
if (Number.isInteger(declaredCount) && declaredCount !== sectionCount) {
|
|
297
|
+
defects.push({
|
|
298
|
+
code: "action-count-inconsistency",
|
|
299
|
+
message: `The document declares ${declaredCount} open action${declaredCount === 1 ? "" : "s"}, but the Remaining Actions section contains ${sectionCount} dated/listed item${sectionCount === 1 ? "" : "s"}. Reconcile the summary and action table before delivery.`,
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { ok: defects.length === 0, defects, actionSectionItemCount: sectionCount };
|
|
306
|
+
}
|
|
307
|
+
|
|
251
308
|
async function collectSourceDocuments(commandCwd) {
|
|
252
309
|
const documents = [];
|
|
253
310
|
let totalBytes = 0;
|
|
@@ -419,6 +476,7 @@ export async function validateWordDocumentArtifacts({
|
|
|
419
476
|
if (artifact.extension === ".pdf") {
|
|
420
477
|
const extracted = await extractPdf(artifact);
|
|
421
478
|
const semantic = evaluateCurrentStateText({ sourceText, outputText: extracted.text, currentStateRequired });
|
|
479
|
+
const consistency = evaluateDocumentConsistency(extracted.text);
|
|
422
480
|
const pageBalance = evaluatePdfPageBalance(extracted.bbox);
|
|
423
481
|
if (!String(extracted.text || "").trim()) {
|
|
424
482
|
defects.push({
|
|
@@ -435,6 +493,7 @@ export async function validateWordDocumentArtifacts({
|
|
|
435
493
|
});
|
|
436
494
|
}
|
|
437
495
|
defects.push(...semantic.defects.map((item) => ({ ...item, path: artifact.path })));
|
|
496
|
+
defects.push(...consistency.defects.map((item) => ({ ...item, path: artifact.path })));
|
|
438
497
|
defects.push(...pageBalance.defects.map((item) => ({ ...item, path: artifact.path })));
|
|
439
498
|
artifactReports.push({
|
|
440
499
|
path: artifact.path,
|
|
@@ -442,11 +501,13 @@ export async function validateWordDocumentArtifacts({
|
|
|
442
501
|
textChars: extracted.text.length,
|
|
443
502
|
pageCount: pageBalance.pages.length,
|
|
444
503
|
pages: pageBalance.pages,
|
|
504
|
+
actionSectionItemCount: consistency.actionSectionItemCount,
|
|
445
505
|
supersededLiterals: semantic.supersededLiterals,
|
|
446
506
|
});
|
|
447
507
|
} else {
|
|
448
508
|
const text = await extractDocxText(artifact);
|
|
449
509
|
const semantic = evaluateCurrentStateText({ sourceText, outputText: text, currentStateRequired });
|
|
510
|
+
const consistency = evaluateDocumentConsistency(text);
|
|
450
511
|
if (!String(text || "").trim()) {
|
|
451
512
|
defects.push({
|
|
452
513
|
code: "empty-docx-text",
|
|
@@ -455,10 +516,12 @@ export async function validateWordDocumentArtifacts({
|
|
|
455
516
|
});
|
|
456
517
|
}
|
|
457
518
|
defects.push(...semantic.defects.map((item) => ({ ...item, path: artifact.path })));
|
|
519
|
+
defects.push(...consistency.defects.map((item) => ({ ...item, path: artifact.path })));
|
|
458
520
|
artifactReports.push({
|
|
459
521
|
path: artifact.path,
|
|
460
522
|
extension: artifact.extension,
|
|
461
523
|
textChars: text.length,
|
|
524
|
+
actionSectionItemCount: consistency.actionSectionItemCount,
|
|
462
525
|
supersededLiterals: semantic.supersededLiterals,
|
|
463
526
|
});
|
|
464
527
|
}
|
package/src/task-profiles.js
CHANGED
|
@@ -276,7 +276,7 @@ export const TASK_PROFILES = {
|
|
|
276
276
|
id: "word",
|
|
277
277
|
label: "Word documents",
|
|
278
278
|
prompt:
|
|
279
|
-
"Bias toward Word/docx/document workflows while still using writing, conversion, LaTeX, or scripts when useful. Preserve source material byte-for-byte and synthesize a reader-facing document from the authoritative current facts instead of concatenating notes, logs, schemas, task IDs, private paths, or delivery instructions. Reconcile conflicting or superseded source facts before drafting: values labeled corrected, replaced, earlier, old, prior, cancelled, or no longer selected identify what to discard, and those discarded literals must not appear in the final document unless the user explicitly requests history or the history is necessary to explain a live decision. Keep the reconciliation history private: a current-state handoff should say who owns the work, which value is approved, and which option is selected, not narrate who was replaced or which preliminary value is no longer used. Before finishing, extract the final text and search for every superseded literal found in the sources. Prefer mature editable-document tooling already available in the workspace, such as python-docx, pandoc, or LibreOffice, over hand-written OOXML; when direct OOXML is genuinely necessary, validate its package parts and openability. Keep one maintainable source of truth and a reproducible project-local build command. Verify the DOCX is structurally editable, compile the PDF, run pdftotext or an equivalent extraction check that rejects replacement characters and unexpected control glyphs, and verify PDF text bounding boxes remain inside a readable page margin. Render every PDF page to a separate image under an ignored build/verification directory. Inspect one rendered page per read_image call, never batch pages into one vision call, and repair orphaned headings, near-empty spill pages, awkward table or paragraph breaks, overlaps, clipping, excessive whitespace, weak hierarchy, and inconsistent number formatting before finishing. Keep normal readable body type, line spacing, and margins; never shrink typography merely to force the document onto one page. When content spans pages, move or redistribute coherent sections so each page is useful instead of leaving a short spill page. Preserve an intentionally sparse appendix, approval, signature, reference, or back-matter page when it serves a real purpose. Retain ignored verification renders as evidence; optional cleanup must never block completion. Use clear descriptive filenames, exclude caches and generated debris from commits, inspect git status/diff before committing, and
|
|
279
|
+
"Bias toward Word/docx/document workflows while still using writing, conversion, LaTeX, or scripts when useful. Preserve source material byte-for-byte and synthesize a reader-facing document from the authoritative current facts instead of concatenating notes, logs, schemas, task IDs, private paths, or delivery instructions. Reconcile conflicting or superseded source facts before drafting: values labeled corrected, replaced, earlier, old, prior, cancelled, or no longer selected identify what to discard, and those discarded literals must not appear in the final document unless the user explicitly requests history or the history is necessary to explain a live decision. Keep the reconciliation history private: a current-state handoff should say who owns the work, which value is approved, and which option is selected, not narrate who was replaced or which preliminary value is no longer used. Before finishing, extract the final text and search for every superseded literal found in the sources. Cross-check every quantitative statement against the detailed section it summarizes: counts of open actions, risks, decisions, line items, totals, dates, and owners must agree everywhere. Prefer mature editable-document tooling already available in the workspace, such as python-docx, pandoc, or LibreOffice, over hand-written OOXML; when direct OOXML is genuinely necessary, validate its package parts and openability. Keep one maintainable source of truth and a reproducible project-local build command. When reproducibility is requested, declare dependencies in project files and make the exact documented command work from a clean project shell; do not rely on inherited packages or claim that dependencies are already installed merely because the current agent container has them. Run that exact documented command, not only its inner generator. Verify the DOCX is structurally editable, compile the PDF, run pdftotext or an equivalent extraction check that rejects replacement characters and unexpected control glyphs, and verify PDF text bounding boxes remain inside a readable page margin. Render every PDF page to a separate image under an ignored build/verification directory. Inspect one rendered page per read_image call, never batch pages into one vision call, and repair orphaned headings, near-empty spill pages, awkward table or paragraph breaks, overlaps, clipping, excessive whitespace, weak hierarchy, and inconsistent number formatting before finishing. Keep normal readable body type, line spacing, and margins; never shrink typography merely to force the document onto one page. When content spans pages, move or redistribute coherent sections so each page is useful instead of leaving a short spill page. Preserve an intentionally sparse appendix, approval, signature, reference, or back-matter page when it serves a real purpose. Retain ignored verification renders as evidence; optional cleanup must never block completion. Use clear descriptive filenames, exclude caches and generated debris from commits, inspect git status/diff before committing, and honor any project instruction that requires an intentional clean commit even when the chat did not repeat it. Report success only after the editable source, reader-facing current-state content, internal consistency, visual layout, searchable text, exact documented build command, and requested artifacts all pass.",
|
|
280
280
|
tools: ["files", "shell", "canvas", "sandbox"],
|
|
281
281
|
},
|
|
282
282
|
latex: {
|