newmark-agent 0.4.0 → 0.4.2
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/dist/conversation-utility-host.bundle.cjs +112 -1
- package/dist/core/agent.js +2 -0
- package/dist/core/toolPolicy.d.ts +9 -0
- package/dist/core/toolPolicy.js +125 -0
- package/dist/toolchain/registry-seeder.js +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +28 -0
- package/dist/tools/nativeTools.js +1 -0
- package/dist/tui/src/app.js +24 -0
- package/dist/tui/src/i18n.js +151 -0
- package/dist/tui/src/render.js +152 -61
- package/dist/tui/src/state.js +83 -0
- package/dist/ui/index.html +172 -62
- package/dist/ui/lucide-sprite.svg +5 -0
- package/dist/wsl-agent-host.bundle.cjs +112 -1
- package/package.json +4 -10
- package/Flow/Electron-Debug-Release.Flow.json +0 -43
- package/Flow/Flow.md +0 -9
- package/Flow/UI-Feature-Integration.Flow.json +0 -96
|
@@ -327541,6 +327541,7 @@ var NATIVE_TOOL_CATALOG = [
|
|
|
327541
327541
|
{ name: "read", label: "Read file", description: "Read workspace file contents.", category: "core", defaultEnabled: true, protected: true, availability: "required" },
|
|
327542
327542
|
{ name: "write", label: "Write file", description: "Create or overwrite workspace files.", category: "core", defaultEnabled: true },
|
|
327543
327543
|
{ name: "edit", label: "Edit file", description: "Patch workspace files through exact find and replace.", category: "core", defaultEnabled: true },
|
|
327544
|
+
{ name: "delete_file", label: "Delete file", description: "Delete one file at a time under Agent supervision; refuses directory and wildcard deletion.", category: "core", defaultEnabled: true },
|
|
327544
327545
|
{ name: "glob", label: "Glob files", description: "Find files by glob pattern.", category: "core", defaultEnabled: true, protected: true, availability: "required" },
|
|
327545
327546
|
{ name: "grep", label: "Search files", description: "Search workspace text by regex.", category: "core", defaultEnabled: true, protected: true, availability: "required" },
|
|
327546
327547
|
{ name: "web_search", label: "Web search", description: "Search the web from the Agent.", category: "web", defaultEnabled: true },
|
|
@@ -335289,6 +335290,91 @@ function planModePolicyPrompt() {
|
|
|
335289
335290
|
"Runtime policy rejects stale or hidden mutating tool calls even if a prompt asks for them."
|
|
335290
335291
|
].join(" ");
|
|
335291
335292
|
}
|
|
335293
|
+
var DELETE_VERB_SOURCE = "(?:remove-item|rmdir|unlink|erase|del|rm|rd|ri)";
|
|
335294
|
+
var DELETE_VERB_BOUNDARY = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}(?:\\s|$)`, "i");
|
|
335295
|
+
function hasDeletionVerb(text) {
|
|
335296
|
+
return DELETE_VERB_BOUNDARY.test(text);
|
|
335297
|
+
}
|
|
335298
|
+
function deletionVerbCount(text) {
|
|
335299
|
+
const matches = text.match(new RegExp(DELETE_VERB_BOUNDARY.source, "gi"));
|
|
335300
|
+
return matches ? matches.length : 0;
|
|
335301
|
+
}
|
|
335302
|
+
function hasLoopDeletion(text) {
|
|
335303
|
+
const lower = text.toLowerCase();
|
|
335304
|
+
if (/\bforeach\b/.test(lower)) return true;
|
|
335305
|
+
if (/\bfor\b\s*[$({]/.test(lower)) return true;
|
|
335306
|
+
if (/\bfor\b\s+\S+\s+in\b/.test(lower)) return true;
|
|
335307
|
+
if (/\bwhile\b\s*[({]/.test(lower)) return true;
|
|
335308
|
+
if (/\bwhile\b\s+\S/.test(lower) && /\bdo\b/.test(lower)) return true;
|
|
335309
|
+
if (/\bdone\b/.test(lower)) return true;
|
|
335310
|
+
return false;
|
|
335311
|
+
}
|
|
335312
|
+
function hasFindXargsDeletion(text) {
|
|
335313
|
+
if (/\bfind\b[^\n;&|]*-(?:delete\b|exec(?:dir)?\s+(?:rm|del|erase)\b)/i.test(text)) return true;
|
|
335314
|
+
if (/\bxargs\b[^\n;&|]*\b(?:rm|del|erase|remove-item)\b/i.test(text)) return true;
|
|
335315
|
+
return false;
|
|
335316
|
+
}
|
|
335317
|
+
function splitCommandArgs(args) {
|
|
335318
|
+
const tokens = [];
|
|
335319
|
+
const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
|
|
335320
|
+
let m2;
|
|
335321
|
+
while ((m2 = re.exec(args)) !== null) {
|
|
335322
|
+
const token = m2[1] ?? m2[2] ?? m2[3] ?? "";
|
|
335323
|
+
if (token) tokens.push(token);
|
|
335324
|
+
}
|
|
335325
|
+
return tokens;
|
|
335326
|
+
}
|
|
335327
|
+
function hasPipeDeletion(text) {
|
|
335328
|
+
return new RegExp(`\\|\\s*${DELETE_VERB_SOURCE}\\b`, "i").test(text);
|
|
335329
|
+
}
|
|
335330
|
+
function hasRecursiveDeletionFlag(text) {
|
|
335331
|
+
const lower = text.toLowerCase();
|
|
335332
|
+
if (/\brm\b\s+(-[a-z]*r[a-z]*|--recursive)\b/.test(lower)) return true;
|
|
335333
|
+
if (/\bremove-item\b[^\n;&|]*\s+-(?:recurse|r)\b/.test(lower)) return true;
|
|
335334
|
+
if (/\b(?:rmdir|rd)\b\s+(-r\b|\/[s]\b)/.test(lower)) return true;
|
|
335335
|
+
if (/\bdel\b\s+\/[s]\b/.test(lower)) return true;
|
|
335336
|
+
return false;
|
|
335337
|
+
}
|
|
335338
|
+
function hasWildcardDeletionTarget(text) {
|
|
335339
|
+
const segmentRe = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}([\\s][^;&|\\n]*)?`, "gi");
|
|
335340
|
+
let m2;
|
|
335341
|
+
while ((m2 = segmentRe.exec(text)) !== null) {
|
|
335342
|
+
const args = m2[1] || "";
|
|
335343
|
+
for (const token of splitCommandArgs(args)) {
|
|
335344
|
+
if (!token || token.startsWith("-") || /^\/[A-Za-z]/.test(token)) continue;
|
|
335345
|
+
if (/[*?]/.test(token)) return true;
|
|
335346
|
+
}
|
|
335347
|
+
}
|
|
335348
|
+
return false;
|
|
335349
|
+
}
|
|
335350
|
+
function hasMultipleDeleteTargets(text) {
|
|
335351
|
+
const segmentRe = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}([\\s][^;&|\\n]*)?`, "gi");
|
|
335352
|
+
let m2;
|
|
335353
|
+
while ((m2 = segmentRe.exec(text)) !== null) {
|
|
335354
|
+
const args = m2[1] || "";
|
|
335355
|
+
const targets = splitCommandArgs(args).filter((t3) => t3 && !t3.startsWith("-") && !/^\/[A-Za-z]/.test(t3) && !/^(&&|\|\||;|\||&|>|>>|<|2>&1)$/.test(t3));
|
|
335356
|
+
if (targets.length >= 2) return true;
|
|
335357
|
+
}
|
|
335358
|
+
return false;
|
|
335359
|
+
}
|
|
335360
|
+
function evaluateDeletionGuard(command) {
|
|
335361
|
+
const text = String(command || "");
|
|
335362
|
+
if (!text.trim()) return { blocked: false };
|
|
335363
|
+
const findXargs = hasFindXargsDeletion(text);
|
|
335364
|
+
if (!hasDeletionVerb(text) && !findXargs) return { blocked: false };
|
|
335365
|
+
const refuse = (kind) => ({
|
|
335366
|
+
blocked: true,
|
|
335367
|
+
reason: `[deletion guard] ${kind} batch deletion is not allowed. Delete files one by one with the delete_file tool under Agent supervision.`
|
|
335368
|
+
});
|
|
335369
|
+
if (hasLoopDeletion(text)) return refuse("Loop-based");
|
|
335370
|
+
if (findXargs) return refuse("find/xargs");
|
|
335371
|
+
if (hasPipeDeletion(text)) return refuse("Pipe-fed");
|
|
335372
|
+
if (hasRecursiveDeletionFlag(text)) return refuse("Recursive");
|
|
335373
|
+
if (hasWildcardDeletionTarget(text)) return refuse("Wildcard");
|
|
335374
|
+
if (hasMultipleDeleteTargets(text)) return refuse("Multiple-target");
|
|
335375
|
+
if (deletionVerbCount(text) >= 2) return refuse("Multiple-statement");
|
|
335376
|
+
return { blocked: false };
|
|
335377
|
+
}
|
|
335292
335378
|
|
|
335293
335379
|
// src/core/wslHostToolBridge.ts
|
|
335294
335380
|
var ROOT_AGENT_ACTOR_ID = "00000000-0000-4000-8000-000000000001";
|
|
@@ -336165,6 +336251,7 @@ var ToolExecutor = class {
|
|
|
336165
336251
|
t3("read", "Read file contents. Use ABSOLUTE paths. The working directory is given in system prompt.", { path: { type: "string" } }, ["path"]),
|
|
336166
336252
|
t3("write", "Write/create a file. Use ABSOLUTE paths.", { path: { type: "string" }, content: { type: "string" } }, ["path", "content"]),
|
|
336167
336253
|
t3("edit", "Edit file with find-and-replace. Use ABSOLUTE paths.", { path: { type: "string" }, old_str: { type: "string" }, new_str: { type: "string" } }, ["path", "old_str", "new_str"]),
|
|
336254
|
+
t3("delete_file", "Delete ONE file under Agent supervision. Use ABSOLUTE paths. This tool refuses directory deletion and wildcard paths; delete files one by one. Never use bash rm/del/Remove-Item for batch (recursive/wildcard/loop/pipe/multi-target) deletion \u2014 the runtime hard-blocks such commands.", { path: { type: "string" } }, ["path"]),
|
|
336168
336255
|
t3("glob", "Find files by glob pattern (e.g. **/*.ts, src/**/*.html)", { pattern: { type: "string" } }, ["pattern"]),
|
|
336169
336256
|
t3("grep", "Search file content with regex", { pattern: { type: "string" }, path: { type: "string" } }, ["pattern", "path"]),
|
|
336170
336257
|
t3("web_search", "Search the web", { query: { type: "string" } }, ["query"]),
|
|
@@ -336499,6 +336586,7 @@ var ToolExecutor = class {
|
|
|
336499
336586
|
case "read":
|
|
336500
336587
|
case "write":
|
|
336501
336588
|
case "edit":
|
|
336589
|
+
case "delete_file":
|
|
336502
336590
|
case "grep":
|
|
336503
336591
|
case "file_audit":
|
|
336504
336592
|
case "pdf_read":
|
|
@@ -336515,6 +336603,11 @@ var ToolExecutor = class {
|
|
|
336515
336603
|
if (permissionGuard) return permissionGuard;
|
|
336516
336604
|
const bashGuard = tool === "bash" || tool === "terminal_takeover" && g2("action") === "write" ? this.checkBashWorkspaceAccess(g2("command"), context.workspacePath || wsPath) : null;
|
|
336517
336605
|
if (bashGuard) return bashGuard;
|
|
336606
|
+
const deletionGuardTarget = tool === "bash" || tool === "terminal_takeover" && g2("action") === "write" ? g2("command") : null;
|
|
336607
|
+
if (deletionGuardTarget !== null) {
|
|
336608
|
+
const deletionGuard = evaluateDeletionGuard(deletionGuardTarget);
|
|
336609
|
+
if (deletionGuard.blocked) return deletionGuard.reason || "[deletion guard] Batch deletion is not allowed.";
|
|
336610
|
+
}
|
|
336518
336611
|
try {
|
|
336519
336612
|
switch (tool) {
|
|
336520
336613
|
case "bash":
|
|
@@ -336527,6 +336620,8 @@ var ToolExecutor = class {
|
|
|
336527
336620
|
return this.fwrite(resolve16(g2("path")), g2("content"));
|
|
336528
336621
|
case "edit":
|
|
336529
336622
|
return this.fedit(resolve16(g2("path")), g2("old_str"), g2("new_str"));
|
|
336623
|
+
case "delete_file":
|
|
336624
|
+
return this.fdelete(resolve16(g2("path")));
|
|
336530
336625
|
case "glob":
|
|
336531
336626
|
return this.glob(g2("pattern"), wsPath);
|
|
336532
336627
|
case "grep":
|
|
@@ -337060,6 +337155,20 @@ var ToolExecutor = class {
|
|
|
337060
337155
|
return `[edit] ${e3}`;
|
|
337061
337156
|
}
|
|
337062
337157
|
}
|
|
337158
|
+
fdelete(p) {
|
|
337159
|
+
try {
|
|
337160
|
+
if (/[*?]/.test(p)) return "[delete_file] Refused: wildcard paths are not allowed. Delete one file per call.";
|
|
337161
|
+
const resolved = path15.resolve(p);
|
|
337162
|
+
const stat = fs13.lstatSync(resolved);
|
|
337163
|
+
if (stat.isDirectory()) {
|
|
337164
|
+
return "[delete_file] Refused: deleting a directory is not allowed. Delete files one by one under Agent supervision.";
|
|
337165
|
+
}
|
|
337166
|
+
fs13.unlinkSync(resolved);
|
|
337167
|
+
return `[delete_file] OK: ${resolved}`;
|
|
337168
|
+
} catch (e3) {
|
|
337169
|
+
return `[delete_file] ${e3 instanceof Error ? e3.message : String(e3)}`;
|
|
337170
|
+
}
|
|
337171
|
+
}
|
|
337063
337172
|
glob(pattern, ws) {
|
|
337064
337173
|
try {
|
|
337065
337174
|
const results = globSync(pattern, {
|
|
@@ -339928,7 +340037,7 @@ var DOMAIN_PREFIXES = [
|
|
|
339928
340037
|
[/^web_/, "web"],
|
|
339929
340038
|
[/^computer_use$/, "computer"],
|
|
339930
340039
|
[/^(image_|ocr_|pdf_)/, "media"],
|
|
339931
|
-
[/^(bash|pwd|read|write|edit|glob|grep)$/, "core"]
|
|
340040
|
+
[/^(bash|pwd|read|write|edit|delete_file|glob|grep)$/, "core"]
|
|
339932
340041
|
];
|
|
339933
340042
|
var READ_TOOL_PATTERN = /^(pwd|read|glob|grep|git_status|git_log|git_diff|git_branch|git_show|memory_lab_read|memory_lab_query|skill|linked_plan|build_history_query|subagent_read|subagent_result|subagent_list|subagent_progress|question|image_inspect|image_display|ocr_read|pdf_read|automation_list|automation_status)$/;
|
|
339934
340043
|
var DESTRUCTIVE_PATTERN = /(?<!\b(?:are|is|be|being|was|were|will be|would be|gets?|become)\s)\b(?:destroy|delete|erase|remove|rm\s|force|shutdown|kill|terminate|drop\s|prune)\b/;
|
|
@@ -344417,6 +344526,7 @@ var CORE_SYSTEM_PROMPT = `You are Newmark Agent, a powerful AI coding assistant
|
|
|
344417
344526
|
- read: Read file contents
|
|
344418
344527
|
- write: Write a new file
|
|
344419
344528
|
- edit: Edit a file with search-and-replace
|
|
344529
|
+
- delete_file: Delete ONE file at a time under Agent supervision (absolute path; refuses directories and wildcards)
|
|
344420
344530
|
- glob: Find files by pattern
|
|
344421
344531
|
- grep: Search file contents with regex
|
|
344422
344532
|
- web_search: Search the web via DuckDuckGo
|
|
@@ -344478,6 +344588,7 @@ var CORE_SYSTEM_PROMPT = `You are Newmark Agent, a powerful AI coding assistant
|
|
|
344478
344588
|
- Visible replies must be concise, direct engineering prose. Do not wrap replies in chat bubbles or role labels.
|
|
344479
344589
|
- Be thorough and precise. Verify your work.
|
|
344480
344590
|
- Use tools appropriately - don't just describe, do it.
|
|
344591
|
+
- Deletion safety (intrinsic, non-overridable): file deletion is allowed only ONE file at a time under Agent supervision. Use the delete_file tool with an absolute path for every single-file delete. Never use bash or the terminal to delete files in bulk: recursive deletes (rm -r/-rf, Remove-Item -Recurse, rmdir /s, del /s), wildcard deletes (rm *.log, del *), loop deletes (for/foreach/while + rm), pipe-fed deletes (Get-ChildItem | Remove-Item), find/xargs deletes, and multi-target or multi-statement deletes are hard-blocked by the runtime and will be rejected. To remove a directory, delete its files one by one first, then remove the now-empty directory without a recursive flag.
|
|
344481
344592
|
- For desktop Computer Use requests, follow observe -> decide -> act -> observe. Start visible takeover with computer_use takeover_start before multi-step desktop control and stop it when finished. Prefer app-scoped actions through app_list/app_observe/app_* when controlling one taskbar application, because this preserves human collaboration around other windows. Prefer target_id from the latest high-priority semantic UI objects, otherwise precise coordinates from the latest observation. Use vision plus UI controls together when the selected model has vision input. Avoid destructive UI actions unless the user asked for them, and do not claim YOLO/OCR perception unless an actual detector/OCR result is present.
|
|
344482
344593
|
- For browser buttons and scanned document pages, the strict recognition sequence is text layer/DOM -> screenshot to a validated vision model -> local OCR. Do not skip directly to OCR. If OCR is used, treat it as approximate evidence and repair only what surrounding context supports.
|
|
344483
344594
|
- Multiple tool calls emitted in one provider turn run concurrently. Treat their returned records as one barrier: continue reasoning only after every call in that batch has returned either a successful receipt or a failure receipt.
|
package/dist/core/agent.js
CHANGED
|
@@ -103,6 +103,7 @@ let CORE_SYSTEM_PROMPT = `You are Newmark Agent, a powerful AI coding assistant
|
|
|
103
103
|
- read: Read file contents
|
|
104
104
|
- write: Write a new file
|
|
105
105
|
- edit: Edit a file with search-and-replace
|
|
106
|
+
- delete_file: Delete ONE file at a time under Agent supervision (absolute path; refuses directories and wildcards)
|
|
106
107
|
- glob: Find files by pattern
|
|
107
108
|
- grep: Search file contents with regex
|
|
108
109
|
- web_search: Search the web via DuckDuckGo
|
|
@@ -164,6 +165,7 @@ let CORE_SYSTEM_PROMPT = `You are Newmark Agent, a powerful AI coding assistant
|
|
|
164
165
|
- Visible replies must be concise, direct engineering prose. Do not wrap replies in chat bubbles or role labels.
|
|
165
166
|
- Be thorough and precise. Verify your work.
|
|
166
167
|
- Use tools appropriately - don't just describe, do it.
|
|
168
|
+
- Deletion safety (intrinsic, non-overridable): file deletion is allowed only ONE file at a time under Agent supervision. Use the delete_file tool with an absolute path for every single-file delete. Never use bash or the terminal to delete files in bulk: recursive deletes (rm -r/-rf, Remove-Item -Recurse, rmdir /s, del /s), wildcard deletes (rm *.log, del *), loop deletes (for/foreach/while + rm), pipe-fed deletes (Get-ChildItem | Remove-Item), find/xargs deletes, and multi-target or multi-statement deletes are hard-blocked by the runtime and will be rejected. To remove a directory, delete its files one by one first, then remove the now-empty directory without a recursive flag.
|
|
167
169
|
- For desktop Computer Use requests, follow observe -> decide -> act -> observe. Start visible takeover with computer_use takeover_start before multi-step desktop control and stop it when finished. Prefer app-scoped actions through app_list/app_observe/app_* when controlling one taskbar application, because this preserves human collaboration around other windows. Prefer target_id from the latest high-priority semantic UI objects, otherwise precise coordinates from the latest observation. Use vision plus UI controls together when the selected model has vision input. Avoid destructive UI actions unless the user asked for them, and do not claim YOLO/OCR perception unless an actual detector/OCR result is present.
|
|
168
170
|
- For browser buttons and scanned document pages, the strict recognition sequence is text layer/DOM -> screenshot to a validated vision model -> local OCR. Do not skip directly to OCR. If OCR is used, treat it as approximate evidence and repair only what surrounding context supports.
|
|
169
171
|
- Multiple tool calls emitted in one provider turn run concurrently. Treat their returned records as one barrier: continue reasoning only after every call in that batch has returned either a successful receipt or a failure receipt.
|
|
@@ -25,4 +25,13 @@ export declare function toolAvailability(name: string): ToolAvailability;
|
|
|
25
25
|
export declare function evaluateToolPolicy(request: ToolPolicyRequest): ToolPolicyDecision;
|
|
26
26
|
export declare function filterToolDefinitions<T>(definitions: T[], request: Omit<ToolPolicyRequest, 'name' | 'args'>): T[];
|
|
27
27
|
export declare function planModePolicyPrompt(): string;
|
|
28
|
+
export interface DeletionGuardDecision {
|
|
29
|
+
blocked: boolean;
|
|
30
|
+
reason?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 硬性删除命令审查。blocked=true 表示该命令构成脚本/命令批量删除,必须拒绝并
|
|
34
|
+
* 引导 Agent 使用受监管的 delete_file 工具逐个删除。
|
|
35
|
+
*/
|
|
36
|
+
export declare function evaluateDeletionGuard(command: string): DeletionGuardDecision;
|
|
28
37
|
//# sourceMappingURL=toolPolicy.d.ts.map
|
package/dist/core/toolPolicy.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.toolAvailability = toolAvailability;
|
|
|
7
7
|
exports.evaluateToolPolicy = evaluateToolPolicy;
|
|
8
8
|
exports.filterToolDefinitions = filterToolDefinitions;
|
|
9
9
|
exports.planModePolicyPrompt = planModePolicyPrompt;
|
|
10
|
+
exports.evaluateDeletionGuard = evaluateDeletionGuard;
|
|
10
11
|
const REQUIRED_TOOLS = new Set(['pwd', 'read', 'glob', 'grep']);
|
|
11
12
|
const MODE_SCOPED_TOOLS = new Set([
|
|
12
13
|
'image_inspect',
|
|
@@ -167,4 +168,128 @@ function planModePolicyPrompt() {
|
|
|
167
168
|
'Runtime policy rejects stale or hidden mutating tool calls even if a prompt asks for them.',
|
|
168
169
|
].join(' ');
|
|
169
170
|
}
|
|
171
|
+
/** 删除命令动词(跨 POSIX / PowerShell / cmd)。注意:不含单独 "remove"(避免匹配普通英文)。 */
|
|
172
|
+
const DELETE_VERB_SOURCE = '(?:remove-item|rmdir|unlink|erase|del|rm|rd|ri)';
|
|
173
|
+
const DELETE_VERB_BOUNDARY = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}(?:\\s|$)`, 'i');
|
|
174
|
+
function hasDeletionVerb(text) {
|
|
175
|
+
return DELETE_VERB_BOUNDARY.test(text);
|
|
176
|
+
}
|
|
177
|
+
function deletionVerbCount(text) {
|
|
178
|
+
const matches = text.match(new RegExp(DELETE_VERB_BOUNDARY.source, 'gi'));
|
|
179
|
+
return matches ? matches.length : 0;
|
|
180
|
+
}
|
|
181
|
+
/** 循环结构批量删除:foreach / for…in / for( / while( / bash do…done。 */
|
|
182
|
+
function hasLoopDeletion(text) {
|
|
183
|
+
const lower = text.toLowerCase();
|
|
184
|
+
if (/\bforeach\b/.test(lower))
|
|
185
|
+
return true; // PowerShell foreach
|
|
186
|
+
if (/\bfor\b\s*[$({]/.test(lower))
|
|
187
|
+
return true; // PowerShell/C for(...)
|
|
188
|
+
if (/\bfor\b\s+\S+\s+in\b/.test(lower))
|
|
189
|
+
return true; // bash for f in ...
|
|
190
|
+
if (/\bwhile\b\s*[({]/.test(lower))
|
|
191
|
+
return true; // while(...)
|
|
192
|
+
if (/\bwhile\b\s+\S/.test(lower) && /\bdo\b/.test(lower))
|
|
193
|
+
return true; // bash while ... do
|
|
194
|
+
if (/\bdone\b/.test(lower))
|
|
195
|
+
return true; // bash 循环结束标记
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
/** find -delete / find -exec rm / xargs rm 批量删除。 */
|
|
199
|
+
function hasFindXargsDeletion(text) {
|
|
200
|
+
if (/\bfind\b[^\n;&|]*-(?:delete\b|exec(?:dir)?\s+(?:rm|del|erase)\b)/i.test(text))
|
|
201
|
+
return true;
|
|
202
|
+
if (/\bxargs\b[^\n;&|]*\b(?:rm|del|erase|remove-item)\b/i.test(text))
|
|
203
|
+
return true;
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
/** 按 shell 语义切分参数:引号内的空格不拆分,返回去引号后的 token。 */
|
|
207
|
+
function splitCommandArgs(args) {
|
|
208
|
+
const tokens = [];
|
|
209
|
+
const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
|
|
210
|
+
let m;
|
|
211
|
+
while ((m = re.exec(args)) !== null) {
|
|
212
|
+
const token = m[1] ?? m[2] ?? m[3] ?? '';
|
|
213
|
+
if (token)
|
|
214
|
+
tokens.push(token);
|
|
215
|
+
}
|
|
216
|
+
return tokens;
|
|
217
|
+
}
|
|
218
|
+
/** 管道接收端删除:上游产出多项,删除动词作为接收端即批量删除。 */
|
|
219
|
+
function hasPipeDeletion(text) {
|
|
220
|
+
return new RegExp(`\\|\\s*${DELETE_VERB_SOURCE}\\b`, 'i').test(text);
|
|
221
|
+
}
|
|
222
|
+
/** 递归删除标志:rm -r/-R/--recursive、Remove-Item -Recurse、rmdir/rd /s、del /s。 */
|
|
223
|
+
function hasRecursiveDeletionFlag(text) {
|
|
224
|
+
const lower = text.toLowerCase();
|
|
225
|
+
if (/\brm\b\s+(-[a-z]*r[a-z]*|--recursive)\b/.test(lower))
|
|
226
|
+
return true;
|
|
227
|
+
if (/\bremove-item\b[^\n;&|]*\s+-(?:recurse|r)\b/.test(lower))
|
|
228
|
+
return true;
|
|
229
|
+
if (/\b(?:rmdir|rd)\b\s+(-r\b|\/[s]\b)/.test(lower))
|
|
230
|
+
return true;
|
|
231
|
+
if (/\bdel\b\s+\/[s]\b/.test(lower))
|
|
232
|
+
return true;
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
/** 删除命令后跟含通配符的目标 token。 */
|
|
236
|
+
function hasWildcardDeletionTarget(text) {
|
|
237
|
+
const segmentRe = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}([\\s][^;&|\\n]*)?`, 'gi');
|
|
238
|
+
let m;
|
|
239
|
+
while ((m = segmentRe.exec(text)) !== null) {
|
|
240
|
+
const args = m[1] || '';
|
|
241
|
+
for (const token of splitCommandArgs(args)) {
|
|
242
|
+
if (!token || token.startsWith('-') || /^\/[A-Za-z]/.test(token))
|
|
243
|
+
continue;
|
|
244
|
+
if (/[*?]/.test(token))
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
/** 单条删除命令后跟 >= 2 个明确目标(非 flag、非 shell 开关)。 */
|
|
251
|
+
function hasMultipleDeleteTargets(text) {
|
|
252
|
+
const segmentRe = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}([\\s][^;&|\\n]*)?`, 'gi');
|
|
253
|
+
let m;
|
|
254
|
+
while ((m = segmentRe.exec(text)) !== null) {
|
|
255
|
+
const args = m[1] || '';
|
|
256
|
+
const targets = splitCommandArgs(args)
|
|
257
|
+
.filter(t => t && !t.startsWith('-') && !/^\/[A-Za-z]/.test(t) && !/^(&&|\|\||;|\||&|>|>>|<|2>&1)$/.test(t));
|
|
258
|
+
if (targets.length >= 2)
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 硬性删除命令审查。blocked=true 表示该命令构成脚本/命令批量删除,必须拒绝并
|
|
265
|
+
* 引导 Agent 使用受监管的 delete_file 工具逐个删除。
|
|
266
|
+
*/
|
|
267
|
+
function evaluateDeletionGuard(command) {
|
|
268
|
+
const text = String(command || '');
|
|
269
|
+
if (!text.trim())
|
|
270
|
+
return { blocked: false };
|
|
271
|
+
// find -delete / find -exec rm 中,-delete 不含标准删除动词,需在入口单独识别为批量删除意图。
|
|
272
|
+
const findXargs = hasFindXargsDeletion(text);
|
|
273
|
+
if (!hasDeletionVerb(text) && !findXargs)
|
|
274
|
+
return { blocked: false };
|
|
275
|
+
const refuse = (kind) => ({
|
|
276
|
+
blocked: true,
|
|
277
|
+
reason: `[deletion guard] ${kind} batch deletion is not allowed. Delete files one by one with the delete_file tool under Agent supervision.`,
|
|
278
|
+
});
|
|
279
|
+
if (hasLoopDeletion(text))
|
|
280
|
+
return refuse('Loop-based');
|
|
281
|
+
if (findXargs)
|
|
282
|
+
return refuse('find/xargs');
|
|
283
|
+
if (hasPipeDeletion(text))
|
|
284
|
+
return refuse('Pipe-fed');
|
|
285
|
+
if (hasRecursiveDeletionFlag(text))
|
|
286
|
+
return refuse('Recursive');
|
|
287
|
+
if (hasWildcardDeletionTarget(text))
|
|
288
|
+
return refuse('Wildcard');
|
|
289
|
+
if (hasMultipleDeleteTargets(text))
|
|
290
|
+
return refuse('Multiple-target');
|
|
291
|
+
if (deletionVerbCount(text) >= 2)
|
|
292
|
+
return refuse('Multiple-statement');
|
|
293
|
+
return { blocked: false };
|
|
294
|
+
}
|
|
170
295
|
//# sourceMappingURL=toolPolicy.js.map
|
|
@@ -15,7 +15,7 @@ const DOMAIN_PREFIXES = [
|
|
|
15
15
|
[/^web_/, 'web'],
|
|
16
16
|
[/^computer_use$/, 'computer'],
|
|
17
17
|
[/^(image_|ocr_|pdf_)/, 'media'],
|
|
18
|
-
[/^(bash|pwd|read|write|edit|glob|grep)$/, 'core'],
|
|
18
|
+
[/^(bash|pwd|read|write|edit|delete_file|glob|grep)$/, 'core'],
|
|
19
19
|
];
|
|
20
20
|
const READ_TOOL_PATTERN = /^(pwd|read|glob|grep|git_status|git_log|git_diff|git_branch|git_show|memory_lab_read|memory_lab_query|skill|linked_plan|build_history_query|subagent_read|subagent_result|subagent_list|subagent_progress|question|image_inspect|image_display|ocr_read|pdf_read|automation_list|automation_status)$/;
|
|
21
21
|
/**
|
package/dist/tools/index.d.ts
CHANGED
package/dist/tools/index.js
CHANGED
|
@@ -227,6 +227,7 @@ class ToolExecutor {
|
|
|
227
227
|
t('read', 'Read file contents. Use ABSOLUTE paths. The working directory is given in system prompt.', { path: { type: 'string' } }, ['path']),
|
|
228
228
|
t('write', 'Write/create a file. Use ABSOLUTE paths.', { path: { type: 'string' }, content: { type: 'string' } }, ['path', 'content']),
|
|
229
229
|
t('edit', 'Edit file with find-and-replace. Use ABSOLUTE paths.', { path: { type: 'string' }, old_str: { type: 'string' }, new_str: { type: 'string' } }, ['path', 'old_str', 'new_str']),
|
|
230
|
+
t('delete_file', 'Delete ONE file under Agent supervision. Use ABSOLUTE paths. This tool refuses directory deletion and wildcard paths; delete files one by one. Never use bash rm/del/Remove-Item for batch (recursive/wildcard/loop/pipe/multi-target) deletion — the runtime hard-blocks such commands.', { path: { type: 'string' } }, ['path']),
|
|
230
231
|
t('glob', 'Find files by glob pattern (e.g. **/*.ts, src/**/*.html)', { pattern: { type: 'string' } }, ['pattern']),
|
|
231
232
|
t('grep', 'Search file content with regex', { pattern: { type: 'string' }, path: { type: 'string' } }, ['pattern', 'path']),
|
|
232
233
|
t('web_search', 'Search the web', { query: { type: 'string' } }, ['query']),
|
|
@@ -578,6 +579,7 @@ class ToolExecutor {
|
|
|
578
579
|
case 'read':
|
|
579
580
|
case 'write':
|
|
580
581
|
case 'edit':
|
|
582
|
+
case 'delete_file':
|
|
581
583
|
case 'grep':
|
|
582
584
|
case 'file_audit':
|
|
583
585
|
case 'pdf_read':
|
|
@@ -598,6 +600,15 @@ class ToolExecutor {
|
|
|
598
600
|
: null;
|
|
599
601
|
if (bashGuard)
|
|
600
602
|
return bashGuard;
|
|
603
|
+
// 硬性删除审查:允许单文件删除,拒绝脚本/命令批量删除。
|
|
604
|
+
const deletionGuardTarget = tool === 'bash' || (tool === 'terminal_takeover' && g('action') === 'write')
|
|
605
|
+
? g('command')
|
|
606
|
+
: null;
|
|
607
|
+
if (deletionGuardTarget !== null) {
|
|
608
|
+
const deletionGuard = (0, toolPolicy_1.evaluateDeletionGuard)(deletionGuardTarget);
|
|
609
|
+
if (deletionGuard.blocked)
|
|
610
|
+
return deletionGuard.reason || '[deletion guard] Batch deletion is not allowed.';
|
|
611
|
+
}
|
|
601
612
|
try {
|
|
602
613
|
switch (tool) {
|
|
603
614
|
case 'bash': return await this.bash(g('command'), wsPath, args.timeout_ms, context.signal);
|
|
@@ -605,6 +616,7 @@ class ToolExecutor {
|
|
|
605
616
|
case 'read': return this.fread(resolve(g('path')));
|
|
606
617
|
case 'write': return this.fwrite(resolve(g('path')), g('content'));
|
|
607
618
|
case 'edit': return this.fedit(resolve(g('path')), g('old_str'), g('new_str'));
|
|
619
|
+
case 'delete_file': return this.fdelete(resolve(g('path')));
|
|
608
620
|
case 'glob': return this.glob(g('pattern'), wsPath);
|
|
609
621
|
case 'grep': return this.grep(g('pattern'), resolve(g('path')));
|
|
610
622
|
case 'web_search': return await this.wsearch(g('query'), context.signal);
|
|
@@ -1136,6 +1148,22 @@ class ToolExecutor {
|
|
|
1136
1148
|
return `[edit] ${e}`;
|
|
1137
1149
|
}
|
|
1138
1150
|
}
|
|
1151
|
+
fdelete(p) {
|
|
1152
|
+
try {
|
|
1153
|
+
if (/[*?]/.test(p))
|
|
1154
|
+
return '[delete_file] Refused: wildcard paths are not allowed. Delete one file per call.';
|
|
1155
|
+
const resolved = path.resolve(p);
|
|
1156
|
+
const stat = fs.lstatSync(resolved);
|
|
1157
|
+
if (stat.isDirectory()) {
|
|
1158
|
+
return '[delete_file] Refused: deleting a directory is not allowed. Delete files one by one under Agent supervision.';
|
|
1159
|
+
}
|
|
1160
|
+
fs.unlinkSync(resolved);
|
|
1161
|
+
return `[delete_file] OK: ${resolved}`;
|
|
1162
|
+
}
|
|
1163
|
+
catch (e) {
|
|
1164
|
+
return `[delete_file] ${e instanceof Error ? e.message : String(e)}`;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1139
1167
|
glob(pattern, ws) {
|
|
1140
1168
|
try {
|
|
1141
1169
|
const results = globSync(pattern, {
|
|
@@ -11,6 +11,7 @@ exports.NATIVE_TOOL_CATALOG = [
|
|
|
11
11
|
{ name: 'read', label: 'Read file', description: 'Read workspace file contents.', category: 'core', defaultEnabled: true, protected: true, availability: 'required' },
|
|
12
12
|
{ name: 'write', label: 'Write file', description: 'Create or overwrite workspace files.', category: 'core', defaultEnabled: true },
|
|
13
13
|
{ name: 'edit', label: 'Edit file', description: 'Patch workspace files through exact find and replace.', category: 'core', defaultEnabled: true },
|
|
14
|
+
{ name: 'delete_file', label: 'Delete file', description: 'Delete one file at a time under Agent supervision; refuses directory and wildcard deletion.', category: 'core', defaultEnabled: true },
|
|
14
15
|
{ name: 'glob', label: 'Glob files', description: 'Find files by glob pattern.', category: 'core', defaultEnabled: true, protected: true, availability: 'required' },
|
|
15
16
|
{ name: 'grep', label: 'Search files', description: 'Search workspace text by regex.', category: 'core', defaultEnabled: true, protected: true, availability: 'required' },
|
|
16
17
|
{ name: 'web_search', label: 'Web search', description: 'Search the web from the Agent.', category: 'web', defaultEnabled: true },
|
package/dist/tui/src/app.js
CHANGED
|
@@ -17,9 +17,12 @@ const {
|
|
|
17
17
|
createState,
|
|
18
18
|
cycleConversationMode,
|
|
19
19
|
enterConversation,
|
|
20
|
+
enterHistoryEventFocus,
|
|
21
|
+
exitHistoryEventFocus,
|
|
20
22
|
filteredCommands,
|
|
21
23
|
moveFocusHorizontal,
|
|
22
24
|
moveConversationHistoryCursor,
|
|
25
|
+
moveHistoryEventCursor,
|
|
23
26
|
moveInputCursorVertical,
|
|
24
27
|
moveMenuSelection,
|
|
25
28
|
moveSettingChoiceSelection,
|
|
@@ -37,6 +40,7 @@ const {
|
|
|
37
40
|
toggleAgentHistory,
|
|
38
41
|
toggleConversationPinned,
|
|
39
42
|
toggleSelectedBuildBlock,
|
|
43
|
+
toggleSelectedBuildEvent,
|
|
40
44
|
toggleSelected,
|
|
41
45
|
toggleWorkflowDetails,
|
|
42
46
|
validateSelectedModel
|
|
@@ -330,7 +334,21 @@ function start(options = {}) {
|
|
|
330
334
|
Promise.resolve(requestConversationStop(state)).finally(paint);
|
|
331
335
|
} else if (key.name === "tab") {
|
|
332
336
|
state.conversationHistoryFocus = false;
|
|
337
|
+
state.historyEventFocus = false;
|
|
338
|
+
state.historyEventIndex = -1;
|
|
333
339
|
returnToConversationSelection(state);
|
|
340
|
+
} else if (state.historyEventFocus) {
|
|
341
|
+
if (key.name === "left") {
|
|
342
|
+
exitHistoryEventFocus(state);
|
|
343
|
+
} else if (key.name === "up") {
|
|
344
|
+
moveHistoryEventCursor(state, -1);
|
|
345
|
+
} else if (key.name === "down") {
|
|
346
|
+
moveHistoryEventCursor(state, 1);
|
|
347
|
+
} else if (key.name === "return" || key.name === "space") {
|
|
348
|
+
toggleSelectedBuildEvent(state);
|
|
349
|
+
}
|
|
350
|
+
} else if (key.name === "right") {
|
|
351
|
+
enterHistoryEventFocus(state);
|
|
334
352
|
} else if (key.name === "up") {
|
|
335
353
|
moveConversationHistoryCursor(state, -1);
|
|
336
354
|
} else if (key.name === "down") {
|
|
@@ -490,6 +508,12 @@ function start(options = {}) {
|
|
|
490
508
|
state.focusRegion = "content";
|
|
491
509
|
state.contentColumn = 0;
|
|
492
510
|
state.notice = "Search Memory Lab tags";
|
|
511
|
+
} else if (state.view === "memory" && !state.inputMode && (str === "o" || str === "O")) {
|
|
512
|
+
state.notice = "Opening Memory Lab Overview";
|
|
513
|
+
var overviewResult = state.adapter.openMemoryOverview();
|
|
514
|
+
if (overviewResult && typeof overviewResult.then === "function") {
|
|
515
|
+
overviewResult.catch(function(error) { state.notice = "Overview failed: " + error.message; }).finally(paint);
|
|
516
|
+
}
|
|
493
517
|
} else if (key.name === "tab") {
|
|
494
518
|
state.focusRegion = "menu";
|
|
495
519
|
state.contentColumn = 0;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight terminal i18n for the Newmark TUI.
|
|
5
|
+
*
|
|
6
|
+
* The TUI chrome (navigation, section headers, view titles, and hints) is
|
|
7
|
+
* rendered in the language chosen under Settings → General → Language. Model,
|
|
8
|
+
* tool, provider, conversation, and workspace names are user data and stay
|
|
9
|
+
* verbatim. Keys are the English source strings, so an untranslated string
|
|
10
|
+
* silently falls back to English rather than dropping text.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const ZH = Object.freeze({
|
|
14
|
+
// Sidebar sections
|
|
15
|
+
"WORKSPACES": "工作区",
|
|
16
|
+
"OPERATIONS": "操作",
|
|
17
|
+
"ACTIVE TARGET": "当前目标",
|
|
18
|
+
"ACTIVE CONVERSATION": "当前对话",
|
|
19
|
+
"Agent TUI": "Agent 终端",
|
|
20
|
+
|
|
21
|
+
// Navigation labels (TUI/src/data.js)
|
|
22
|
+
"Conversations": "对话",
|
|
23
|
+
"Plan": "计划",
|
|
24
|
+
"Goal": "目标",
|
|
25
|
+
"Subagents": "子代理",
|
|
26
|
+
"Model": "模型",
|
|
27
|
+
"Flow Bar": "流程条",
|
|
28
|
+
"Flow List": "流程列表",
|
|
29
|
+
"Flow Task": "流程任务",
|
|
30
|
+
"Tools": "工具",
|
|
31
|
+
"Memory Lab": "记忆实验室",
|
|
32
|
+
"Automations": "自动化",
|
|
33
|
+
"WorkFlow": "工作流",
|
|
34
|
+
"Settings": "设置",
|
|
35
|
+
"Help": "帮助",
|
|
36
|
+
|
|
37
|
+
// Conversation context
|
|
38
|
+
"Conversation plan": "对话计划",
|
|
39
|
+
"Conversation goal": "对话目标",
|
|
40
|
+
"Conversation subagents": "对话子代理",
|
|
41
|
+
"Model and reasoning effort": "模型与推理档位",
|
|
42
|
+
"Flow bar": "流程条",
|
|
43
|
+
"Flow list": "流程列表",
|
|
44
|
+
"Flow task": "流程任务",
|
|
45
|
+
|
|
46
|
+
// Model view
|
|
47
|
+
"Reasoning effort": "推理档位",
|
|
48
|
+
"Deployment": "部署",
|
|
49
|
+
"Shared GUI/TUI request tier · ←/→ changes section": "GUI/TUI 共享请求档位 · ←/→ 切换区块",
|
|
50
|
+
"Used by this conversation, including its Plan and Subagents": "当前对话使用(含计划与子代理)",
|
|
51
|
+
"Enter applies the focused tier or deployment. Effort persists globally; deployments remain per conversation.":
|
|
52
|
+
"Enter 应用当前档位或部署。档位全局持久化,部署按对话保存。",
|
|
53
|
+
|
|
54
|
+
// Memory Lab
|
|
55
|
+
"Tags": "标签",
|
|
56
|
+
"Selected tag": "已选标签",
|
|
57
|
+
"Child tags": "子标签",
|
|
58
|
+
"Memory components": "记忆组件",
|
|
59
|
+
"Core memory": "核心记忆",
|
|
60
|
+
"No memory components": "无记忆组件",
|
|
61
|
+
"No component selected": "未选择组件",
|
|
62
|
+
"Overview": "总览",
|
|
63
|
+
"Memory overview": "记忆总览",
|
|
64
|
+
"Memory tag": "记忆标签",
|
|
65
|
+
|
|
66
|
+
// Chat view
|
|
67
|
+
"N new": "N 新建",
|
|
68
|
+
"Workspace": "工作区",
|
|
69
|
+
"Current": "当前",
|
|
70
|
+
"Preview": "预览",
|
|
71
|
+
"Current conversation": "当前对话",
|
|
72
|
+
"Enter to open this conversation": "Enter 打开此对话",
|
|
73
|
+
"Type a message · Shift+Enter newline · Enter send": "输入消息 · Shift+Enter 换行 · Enter 发送",
|
|
74
|
+
"Select a conversation · Enter to edit": "选择对话 · Enter 编辑",
|
|
75
|
+
|
|
76
|
+
// Settings categories
|
|
77
|
+
"General": "通用",
|
|
78
|
+
"Personalization": "个性化",
|
|
79
|
+
"Runtime": "运行时",
|
|
80
|
+
"Providers": "服务商",
|
|
81
|
+
"Models": "模型",
|
|
82
|
+
"Archive": "归档",
|
|
83
|
+
"Updates": "更新",
|
|
84
|
+
|
|
85
|
+
// Settings
|
|
86
|
+
"Categories": "分类",
|
|
87
|
+
"Language": "语言",
|
|
88
|
+
"Input mode": "输入模式",
|
|
89
|
+
"Conversation style": "对话风格",
|
|
90
|
+
"Option feedback": "选项反馈",
|
|
91
|
+
"Close behavior": "关闭行为",
|
|
92
|
+
"Expand tool usage": "展开工具使用",
|
|
93
|
+
"Theme": "主题",
|
|
94
|
+
"Application font": "应用字体",
|
|
95
|
+
"Font color": "字体颜色",
|
|
96
|
+
"Background color": "背景颜色",
|
|
97
|
+
"Glass intensity": "玻璃强度",
|
|
98
|
+
"Agent backend": "Agent 后端",
|
|
99
|
+
"WSL distribution": "WSL 发行版",
|
|
100
|
+
"Terminal timeout cap": "终端超时上限",
|
|
101
|
+
"Default shell": "默认 Shell",
|
|
102
|
+
"Automatic archive": "自动归档",
|
|
103
|
+
"Retention": "保留期",
|
|
104
|
+
"Include Memory Lab": "包含记忆实验室",
|
|
105
|
+
"Export format": "导出格式",
|
|
106
|
+
"Update channel": "更新通道",
|
|
107
|
+
"Automatic checks": "自动检查",
|
|
108
|
+
"Automatic download": "自动下载",
|
|
109
|
+
"Update source": "更新来源",
|
|
110
|
+
"Live color preview": "实时颜色预览",
|
|
111
|
+
|
|
112
|
+
// Plan / Goal / Agents
|
|
113
|
+
"Linked Plan": "关联计划",
|
|
114
|
+
"Next handoff": "下一交接",
|
|
115
|
+
"Linked goal": "关联目标",
|
|
116
|
+
"No active goal": "无活动目标",
|
|
117
|
+
"RESULT": "结果",
|
|
118
|
+
"No messages recorded yet.": "暂无消息记录。",
|
|
119
|
+
"Records come from the active Newmark conversation": "记录来自当前 Newmark 对话",
|
|
120
|
+
|
|
121
|
+
// Tools
|
|
122
|
+
"Tools & connectors": "工具与连接器",
|
|
123
|
+
"Live runtime": "实时运行时",
|
|
124
|
+
"Safety boundary": "安全边界",
|
|
125
|
+
|
|
126
|
+
// Automation / Workflow
|
|
127
|
+
"[+] New automation": "[+] 新建自动化",
|
|
128
|
+
"[+] New workflow": "[+] 新建工作流",
|
|
129
|
+
"No workflows configured.": "未配置工作流。",
|
|
130
|
+
|
|
131
|
+
// Help
|
|
132
|
+
"SHORTCUT GUIDE": "快捷键指南",
|
|
133
|
+
"Navigation": "导航",
|
|
134
|
+
"Conversation editing": "对话编辑",
|
|
135
|
+
"Running work": "运行中的工作",
|
|
136
|
+
"Operation content": "操作内容",
|
|
137
|
+
"Global": "全局"
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
function resolveLanguage(state) {
|
|
141
|
+
const value = String(state?.settings?.general?.language || "Auto");
|
|
142
|
+
return value === "中文" || value === "zh" ? "zh" : "en";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function tr(state, text) {
|
|
146
|
+
const value = String(text ?? "");
|
|
147
|
+
if (resolveLanguage(state) === "zh") return ZH[value] || value;
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = { resolveLanguage, tr };
|