@lazyingart/agintiflow 0.20.11 → 0.20.12
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 +1 -1
- package/public/app.js +8 -1
- package/scripts/smoke-cli-chat.js +20 -0
- package/src/agent-runner.js +51 -4
- package/src/interactive-cli.js +9 -1
- package/src/model-client.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a web-first coding agent and CLI with DeepSeek routing, sandboxed tools, model providers, canvas artifacts, and optional wrappers.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/public/app.js
CHANGED
|
@@ -1441,8 +1441,15 @@ function isMarkdownBlockStart(line, nextLine = "") {
|
|
|
1441
1441
|
);
|
|
1442
1442
|
}
|
|
1443
1443
|
|
|
1444
|
+
function unwrapRenderableMarkdownFence(value = "") {
|
|
1445
|
+
const raw = String(value || "");
|
|
1446
|
+
const trimmed = raw.trim();
|
|
1447
|
+
const match = trimmed.match(/^```(?:markdown|md)\s*\n([\s\S]*?)\n```\s*$/i);
|
|
1448
|
+
return match ? match[1] : raw;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1444
1451
|
function renderMarkdown(value) {
|
|
1445
|
-
const lines =
|
|
1452
|
+
const lines = unwrapRenderableMarkdownFence(value).replace(/\r\n?/g, "\n").split("\n");
|
|
1446
1453
|
const html = [];
|
|
1447
1454
|
let paragraph = [];
|
|
1448
1455
|
let listType = "";
|
|
@@ -81,6 +81,25 @@ try {
|
|
|
81
81
|
if ((renderedMarkdown.match(/Present/g) || []).length !== 1) {
|
|
82
82
|
throw new Error("terminal markdown renderer duplicated table rows");
|
|
83
83
|
}
|
|
84
|
+
const renderedMarkdownFence = stripMarkdown(
|
|
85
|
+
[
|
|
86
|
+
"```markdown",
|
|
87
|
+
"# Saved Story",
|
|
88
|
+
"",
|
|
89
|
+
"| File | Status |",
|
|
90
|
+
"| --- | --- |",
|
|
91
|
+
"| `story-ja.md` | **saved** |",
|
|
92
|
+
"```",
|
|
93
|
+
].join("\n")
|
|
94
|
+
);
|
|
95
|
+
if (
|
|
96
|
+
renderedMarkdownFence.includes("code markdown") ||
|
|
97
|
+
renderedMarkdownFence.includes("| --- |") ||
|
|
98
|
+
!renderedMarkdownFence.includes("Saved Story") ||
|
|
99
|
+
!renderedMarkdownFence.includes("story-ja.md")
|
|
100
|
+
) {
|
|
101
|
+
throw new Error("terminal markdown renderer did not unwrap full markdown fences");
|
|
102
|
+
}
|
|
84
103
|
const renderedDiff = stripMarkdown(
|
|
85
104
|
[
|
|
86
105
|
"Diff:",
|
|
@@ -291,6 +310,7 @@ try {
|
|
|
291
310
|
projectRoot: tempRoot,
|
|
292
311
|
checks: [
|
|
293
312
|
"markdown-render",
|
|
313
|
+
"markdown-fence-render",
|
|
294
314
|
"markdown-table-no-duplicate",
|
|
295
315
|
"patch-diff-render",
|
|
296
316
|
"workspace-patch-event-render",
|
package/src/agent-runner.js
CHANGED
|
@@ -313,7 +313,7 @@ async function createInitialState(config, sessionId) {
|
|
|
313
313
|
? "Host tmux tools are available for long-running terminals: list sessions, capture panes, send safe keys/text, and start detached sessions. Prefer these tools for monitoring long installs/tests/dev servers without blocking; capture before sending input and never send secrets or sudo passwords. Do not start or install tmux inside Docker run_command containers because those containers are short-lived."
|
|
314
314
|
: "",
|
|
315
315
|
config.allowFileTools
|
|
316
|
-
? `Workspace file tools are available in ${config.commandCwd}: inspect_project, list_files, read_file, search_files, write_file, apply_patch, open_workspace_file, and preview_workspace. For large or unfamiliar repositories, call inspect_project first, then search/read AGINTI.md/AGENTS.md/README/manifests as relevant before editing. apply_patch supports exact single-file replacements plus Codex-style/unified multi-file patches; prefer it for source edits after reading/searching the relevant context. Always use workspace-relative paths such as plot_fx.svg or docs/report.tex, never absolute host paths. Secret paths, .git internals, node_modules writes, and huge files are blocked. For generated local websites/pages, use open_workspace_file or preview_workspace instead of starting a localhost server inside Docker.`
|
|
316
|
+
? `Workspace file tools are available in ${config.commandCwd}: inspect_project, list_files, read_file, search_files, write_file, apply_patch, open_workspace_file, and preview_workspace. For large or unfamiliar repositories, call inspect_project first, then search/read AGINTI.md/AGENTS.md/README/manifests as relevant before editing. apply_patch supports exact single-file replacements plus Codex-style/unified multi-file patches; prefer it for source edits after reading/searching the relevant context. Always use workspace-relative paths such as plot_fx.svg or docs/report.tex, never absolute host paths. For newly generated standalone prose/docs/stories/assets, choose a descriptive non-conflicting filename from the topic/language and use mode=create; do not overwrite existing files unless the user explicitly asked to update/replace/overwrite that file. Secret paths, .git internals, node_modules writes, and huge files are blocked. For generated local websites/pages, use open_workspace_file or preview_workspace instead of starting a localhost server inside Docker.`
|
|
317
317
|
: "No workspace file tools are available.",
|
|
318
318
|
config.allowWrapperTools
|
|
319
319
|
? `External coding-agent wrappers are available as advisory tools only. Use the selected wrapper only: ${normalizeWrapperName(config.preferredWrapper)}. Wrapper status: ${wrapperStatusText()}.`
|
|
@@ -362,7 +362,7 @@ async function createInitialState(config, sessionId) {
|
|
|
362
362
|
: `Shell working directory: ${config.commandCwd}`
|
|
363
363
|
: "",
|
|
364
364
|
config.allowFileTools
|
|
365
|
-
? `Workspace file tools enabled in: ${config.commandCwd}. Use inspect_project first for large/unfamiliar codebases. Read AGINTI.md/AGENTS.md/README/manifests when relevant. Use workspace-relative paths. Use apply_patch for code edits; it accepts exact replacements or Codex-style/unified multi-file patches. Local preview tools available: open_workspace_file and preview_workspace.`
|
|
365
|
+
? `Workspace file tools enabled in: ${config.commandCwd}. Use inspect_project first for large/unfamiliar codebases. Read AGINTI.md/AGENTS.md/README/manifests when relevant. Use workspace-relative paths. Use apply_patch for code edits; it accepts exact replacements or Codex-style/unified multi-file patches. For newly generated standalone content, choose descriptive non-conflicting filenames and use mode=create unless the user explicitly asked to overwrite/update. Local preview tools available: open_workspace_file and preview_workspace.`
|
|
366
366
|
: "",
|
|
367
367
|
projectInstructions.exists ? "AGINTI.md project instructions are loaded into system context for this run." : "AGINTI.md is not present unless you create it.",
|
|
368
368
|
config.allowWrapperTools
|
|
@@ -500,7 +500,7 @@ async function applyContinuationPrompt(state, config, observers) {
|
|
|
500
500
|
: `Shell working directory: ${config.commandCwd}. Host platform: ${platformLabel(platform)}. Use OS-compatible commands; prefer WSL/Docker for bash-heavy workflows on Windows.`
|
|
501
501
|
: "",
|
|
502
502
|
config.allowFileTools
|
|
503
|
-
? `Workspace file tools enabled in: ${config.commandCwd}. Use inspect_project first for large or unfamiliar codebases, then search/read exact files before editing. Read AGINTI.md/AGENTS.md/README/manifests when relevant. Use workspace-relative paths. Use apply_patch for code edits; it accepts exact replacements or Codex-style/unified multi-file patches. For generated local files/sites, use open_workspace_file or preview_workspace.`
|
|
503
|
+
? `Workspace file tools enabled in: ${config.commandCwd}. Use inspect_project first for large or unfamiliar codebases, then search/read exact files before editing. Read AGINTI.md/AGENTS.md/README/manifests when relevant. Use workspace-relative paths. Use apply_patch for code edits; it accepts exact replacements or Codex-style/unified multi-file patches. For generated local files/sites, choose descriptive non-conflicting filenames, use mode=create unless the user explicitly asked to overwrite/update, and use open_workspace_file or preview_workspace.`
|
|
504
504
|
: "",
|
|
505
505
|
config.allowWrapperTools
|
|
506
506
|
? `Agent wrappers: selected=${normalizeWrapperName(config.preferredWrapper)}; ${wrapperStatusText()}`
|
|
@@ -601,6 +601,29 @@ function sanitizeToolArgs(toolName, args) {
|
|
|
601
601
|
return safeArgs;
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
function goalClearlyAllowsOverwrite(goal = "") {
|
|
605
|
+
const text = String(goal || "").toLowerCase();
|
|
606
|
+
return (
|
|
607
|
+
/\b(overwrite|replace|update|modify|edit|revise|rewrite|fix|patch|change|append|refresh|regenerate|remember|instruction|instructions|memory|preference|preferences|prefer)\b/i.test(text) ||
|
|
608
|
+
/覆盖|覆寫|替换|替換|更新|修改|修复|修復|编辑|編輯|改写|改寫|追加|记住|記住|指令|说明|說明|偏好|上書き|置換|修正|編集/.test(text)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async function implicitOverwriteBlock(toolName, args, config, state) {
|
|
613
|
+
if (toolName !== "write_file" || args.mode !== "overwrite") return null;
|
|
614
|
+
if (goalClearlyAllowsOverwrite(state?.goal || config.goal || "")) return null;
|
|
615
|
+
const target = resolveWorkspacePath(config, args.path || args.file || "");
|
|
616
|
+
const exists = await fs
|
|
617
|
+
.stat(target.absolutePath)
|
|
618
|
+
.then((stat) => stat.isFile())
|
|
619
|
+
.catch(() => false);
|
|
620
|
+
if (!exists) return null;
|
|
621
|
+
return {
|
|
622
|
+
reason: `Refusing to overwrite existing ${target.relativePath} without an explicit update/replace request. Choose a descriptive new filename or ask the user before replacing it.`,
|
|
623
|
+
category: "workspace-overwrite",
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
604
627
|
function sanitizeToolResult(result) {
|
|
605
628
|
const safeResult = redactValue(result);
|
|
606
629
|
if (typeof safeResult.content === "string") {
|
|
@@ -709,7 +732,7 @@ async function captureSyntheticSnapshot(store, step, config) {
|
|
|
709
732
|
? "Host tmux tools available: tmux_list_sessions, tmux_capture_pane, tmux_send_keys, tmux_start_session. Use them for long-running jobs and agent terminals; capture before sending input. Docker run_command containers are ephemeral, so tmux there will not persist."
|
|
710
733
|
: "",
|
|
711
734
|
config.allowFileTools
|
|
712
|
-
? `Workspace file tools available in: ${config.commandCwd}. Use inspect_project first for large or unfamiliar codebases, then search/read exact files before editing. Use workspace-relative paths. Use apply_patch for code edits; it supports exact single-file replacement and multi-file Codex-style/unified patches.`
|
|
735
|
+
? `Workspace file tools available in: ${config.commandCwd}. Use inspect_project first for large or unfamiliar codebases, then search/read exact files before editing. Use workspace-relative paths. Use apply_patch for code edits; it supports exact single-file replacement and multi-file Codex-style/unified patches. For new standalone generated content, pick a descriptive non-conflicting filename and avoid overwriting unless explicitly requested.`
|
|
713
736
|
: "Workspace file tools disabled.",
|
|
714
737
|
config.allowWrapperTools
|
|
715
738
|
? `Agent wrappers available: selected=${normalizeWrapperName(config.preferredWrapper)}; ${wrapperStatusText()}`
|
|
@@ -805,6 +828,30 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
|
|
|
805
828
|
};
|
|
806
829
|
}
|
|
807
830
|
|
|
831
|
+
const overwriteBlock = await implicitOverwriteBlock(toolCall.function.name, args, config, state);
|
|
832
|
+
if (overwriteBlock) {
|
|
833
|
+
await store.appendEvent("tool.blocked", {
|
|
834
|
+
toolName: toolCall.function.name,
|
|
835
|
+
args: safeArgs,
|
|
836
|
+
reason: overwriteBlock.reason,
|
|
837
|
+
category: overwriteBlock.category,
|
|
838
|
+
});
|
|
839
|
+
observers.event("tool.blocked", {
|
|
840
|
+
toolName: toolCall.function.name,
|
|
841
|
+
args: safeArgs,
|
|
842
|
+
reason: overwriteBlock.reason,
|
|
843
|
+
category: overwriteBlock.category,
|
|
844
|
+
});
|
|
845
|
+
return {
|
|
846
|
+
ok: false,
|
|
847
|
+
blocked: true,
|
|
848
|
+
reason: overwriteBlock.reason,
|
|
849
|
+
category: overwriteBlock.category,
|
|
850
|
+
toolName: toolCall.function.name,
|
|
851
|
+
args: safeArgs,
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
|
|
808
855
|
await store.appendEvent("tool.started", {
|
|
809
856
|
toolName: toolCall.function.name,
|
|
810
857
|
args: safeArgs,
|
package/src/interactive-cli.js
CHANGED
|
@@ -241,7 +241,8 @@ function wrapTextLine(value = "", width = 72) {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
export function stripMarkdown(text) {
|
|
244
|
-
const
|
|
244
|
+
const unwrapped = unwrapRenderableMarkdownFence(text);
|
|
245
|
+
const lines = String(unwrapped || "").split(/\r?\n/);
|
|
245
246
|
let inFence = false;
|
|
246
247
|
let diffContext = 0;
|
|
247
248
|
const rendered = [];
|
|
@@ -304,6 +305,13 @@ export function stripMarkdown(text) {
|
|
|
304
305
|
return rendered.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
305
306
|
}
|
|
306
307
|
|
|
308
|
+
function unwrapRenderableMarkdownFence(text = "") {
|
|
309
|
+
const raw = String(text || "");
|
|
310
|
+
const trimmed = raw.trim();
|
|
311
|
+
const match = trimmed.match(/^```(?:markdown|md)\s*\n([\s\S]*?)\n```\s*$/i);
|
|
312
|
+
return match ? match[1] : raw;
|
|
313
|
+
}
|
|
314
|
+
|
|
307
315
|
function renderPatchLine(line = "", { active = false } = {}) {
|
|
308
316
|
const value = String(line || "");
|
|
309
317
|
if (/^diff --git\s+/.test(value)) return color(value, ansi.bold, ansi.cyan);
|
package/src/model-client.js
CHANGED
|
@@ -480,7 +480,7 @@ export async function createPlan(client, config, state) {
|
|
|
480
480
|
? "Host tmux tools are enabled for long-running sessions. Plan to use tmux_start_session for durable jobs, tmux_capture_pane to monitor, tmux_send_keys to interact after capture, and tmux_list_sessions to discover existing sessions. Do not install or run tmux inside Docker run_command containers; those containers are short-lived and cannot preserve tmux servers."
|
|
481
481
|
: "",
|
|
482
482
|
config.allowFileTools
|
|
483
|
-
? `Workspace file tools are enabled in ${config.commandCwd}: inspect_project, list_files, read_file, search_files, write_file, apply_patch, open_workspace_file, preview_workspace. For large or unfamiliar repos, plan to call inspect_project first, then search/read AGINTI.md/AGENTS.md/README/manifests and exact files. apply_patch supports exact single-file replacements and Codex-style/unified multi-file patches; prefer it for edits after reading relevant context. Keep all paths workspace-relative, for example plot_fx.svg or docs/report.tex, and avoid secrets. For generated local HTML/SVG/PDF/static sites, plan to use open_workspace_file or preview_workspace rather than starting a localhost server inside Docker.`
|
|
483
|
+
? `Workspace file tools are enabled in ${config.commandCwd}: inspect_project, list_files, read_file, search_files, write_file, apply_patch, open_workspace_file, preview_workspace. For large or unfamiliar repos, plan to call inspect_project first, then search/read AGINTI.md/AGENTS.md/README/manifests and exact files. apply_patch supports exact single-file replacements and Codex-style/unified multi-file patches; prefer it for edits after reading relevant context. Keep all paths workspace-relative, for example plot_fx.svg or docs/report.tex, and avoid secrets. For newly generated standalone prose/docs/stories/assets, choose a descriptive non-conflicting filename from the topic/language instead of generic names like story.txt or output.txt; do not overwrite existing files unless the user explicitly asked to update/replace/overwrite that file. For generated local HTML/SVG/PDF/static sites, plan to use open_workspace_file or preview_workspace rather than starting a localhost server inside Docker.`
|
|
484
484
|
: "",
|
|
485
485
|
projectInstructions?.exists
|
|
486
486
|
? `Project instructions: AGINTI.md is loaded from ${projectInstructions.path}${projectInstructions.truncated ? " (truncated)" : ""}. Follow it and update it with file tools when the user asks to remember or change project instructions.`
|
|
@@ -504,6 +504,7 @@ export async function createPlan(client, config, state) {
|
|
|
504
504
|
engineeringGuidance,
|
|
505
505
|
"A canvas/artifacts tunnel is available through send_to_canvas. Use it when an output should be highlighted visually, such as screenshots, image files, important markdown, diffs, or generated artifact paths. It is optional for ordinary text answers.",
|
|
506
506
|
"Work like a practical coding agent: inspect when useful, edit with file tools, run safe checks when they add confidence, and keep outputs inside the workspace.",
|
|
507
|
+
"When saving new standalone content, think of a specific filename from the request and use mode=create. If that name already exists and the user did not ask to replace it, choose a safe variant such as topic-language-v2.md instead of overwriting.",
|
|
507
508
|
"For large apps, websites, LaTeX documents, Python/C/shell projects, or system tasks, plan a coherent minimal implementation, then use tools to create files, run checks, and publish artifacts.",
|
|
508
509
|
"For LaTeX/PDF work, first check whether latexmk or pdflatex already exists in the active host/Docker environment; compile with the existing toolchain before installing packages or rebuilding Docker.",
|
|
509
510
|
"For web search or current information tasks, plan to use browser tools or safe shell network tools when allowed, then preserve useful source notes if the output depends on them.",
|
|
@@ -886,7 +887,7 @@ export async function requestNextStep(client, config, messages) {
|
|
|
886
887
|
function: {
|
|
887
888
|
name: "write_file",
|
|
888
889
|
description:
|
|
889
|
-
"Create or overwrite a small UTF-8 workspace file. Use mode=create for
|
|
890
|
+
"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.",
|
|
890
891
|
parameters: {
|
|
891
892
|
type: "object",
|
|
892
893
|
properties: {
|