mingdao-harness 0.4.1 → 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/package.json +1 -1
- package/presets/local-audit.json +2 -2
- package/src/agent.js +8 -2
- package/src/providers/openai-compatible.js +3 -1
- package/src/web/app.js +21 -1
- package/src/web/index.html +1 -1
package/package.json
CHANGED
package/presets/local-audit.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-audit",
|
|
3
3
|
"label": "本地模型审计",
|
|
4
|
-
"description": "面向本地/资源受限模型(如 131k 窗口 q8 量化)的只读审计预设:只读工具集 +
|
|
4
|
+
"description": "面向本地/资源受限模型(如 131k 窗口 q8 量化)的只读审计预设:只读工具集 + 只读权限 + 保守参数,配合 v0.3.2 本地自适应长任务不中断",
|
|
5
5
|
"systemPrompt": "你是一名代码审计员。任务是对给定代码库做只读审计:先 ls/glob 摸清结构,再 read/grep 逐文件审查,可用 git 查看历史与 diff、fetch 抓取相关文档。输出审计报告:发现的缺陷按严重度分级,每条给出文件:行号证据;不做任何修改。若上下文紧张,先审查最关键的部分并明示未覆盖区域。",
|
|
6
6
|
"tools": ["read", "ls", "glob", "grep", "skill", "git", "fetch", "todo"],
|
|
7
|
-
"permission": "
|
|
7
|
+
"permission": "readonly",
|
|
8
8
|
"maxRounds": 4,
|
|
9
9
|
"maxOutputTokens": 4096
|
|
10
10
|
}
|
package/src/agent.js
CHANGED
|
@@ -232,7 +232,9 @@ export function createAgent({ provider, permission, io, modelName, workingDir, c
|
|
|
232
232
|
count,
|
|
233
233
|
provider,
|
|
234
234
|
executorModel: subagentModel(cfg, modelName),
|
|
235
|
-
|
|
235
|
+
// 可配置触发线:默认远程 80%;本地模型默认 60% 提前压缩——本地推理内存预算有限,
|
|
236
|
+
// 等到 80% 再压时上下文已累积过多、prefill 与内存双高(v0.4.2 本地模型 507 修复)
|
|
237
|
+
triggerRatio: Number(cfg.compactTrigger) > 0 ? cfg.compactTrigger : (caps.isLocal ? 0.6 : undefined),
|
|
236
238
|
force: windowPressure, // v0.3.2:逼近窗口时强制压缩(忽略最小阈值门槛)
|
|
237
239
|
});
|
|
238
240
|
if (compacted) {
|
|
@@ -456,6 +458,10 @@ export function createAgent({ provider, permission, io, modelName, workingDir, c
|
|
|
456
458
|
// 其余模式/工具保持串行,避免多个权限对话框交错。事件顺序(start/render/post/回填)不变。
|
|
457
459
|
const READONLY_BATCH = new Set(['read', 'ls', 'glob', 'grep']);
|
|
458
460
|
const canBatch = permission.mode === 'auto';
|
|
461
|
+
// v0.4.2(本地模型 507 memory_refusal 修复):子代理目标为本地模型时,只读子代理不并行——
|
|
462
|
+
// 多路大 prefill 同时冲进本地推理服务会击穿其单进程内存预算(507 memory_refusal),
|
|
463
|
+
// 串行化只读子代理避免并发峰值(只读 read/ls/glob/grep 仍并行,它们不额外触发大 prefill)。
|
|
464
|
+
const subagentIsLocal = resolveModelCaps(cfg, subagentModel(cfg, modelName)).isLocal;
|
|
459
465
|
|
|
460
466
|
// 预检:解析参数 → PreToolUse 钩子 → 权限检查;拒绝/失败只回填不执行(返回 null)
|
|
461
467
|
// task 工具标记 readOnly 时也可并行(评估 A4:只读子代理 Promise.all)
|
|
@@ -608,7 +614,7 @@ export function createAgent({ provider, permission, io, modelName, workingDir, c
|
|
|
608
614
|
const prep = /** @type {any} */ (await prepTool(tc));
|
|
609
615
|
const name = tc.function?.name || '';
|
|
610
616
|
let batchable = canBatch && Boolean(prep) && !prep.isMcp && READONLY_BATCH.has(name);
|
|
611
|
-
if (!batchable && canBatch && Boolean(prep) && name === 'task' && prep.args?.readOnly === true) batchable = true;
|
|
617
|
+
if (!batchable && canBatch && Boolean(prep) && name === 'task' && prep.args?.readOnly === true && !subagentIsLocal) batchable = true;
|
|
612
618
|
batch.push({ prep, batchable });
|
|
613
619
|
i += 1;
|
|
614
620
|
if (!batchable) break;
|
|
@@ -49,8 +49,10 @@ export async function chat(/** @type {any} */ { baseUrl, apiKey, model, messages
|
|
|
49
49
|
const j = JSON.parse(raw);
|
|
50
50
|
if (j?.error?.message) detail = j.error.message;
|
|
51
51
|
} catch {}
|
|
52
|
+
// 本地模型内存不足(507 memory_refusal):给可操作降级提示而非裸状态码(v0.4.2 本地模型 507 修复)
|
|
53
|
+
const hint = res.status === 507 ? '(本地模型内存不足:请压缩上下文、减少并发子任务,或重启模型服务释放内存)' : '';
|
|
52
54
|
/** @type {ApiError} */
|
|
53
|
-
const e = new Error(`[${model}] API 错误 ${res.status}: ${detail}`);
|
|
55
|
+
const e = new Error(`[${model}] API 错误 ${res.status}: ${detail}${hint}`);
|
|
54
56
|
e.status = res.status;
|
|
55
57
|
e.headers = res.headers; // 重试退避读取 Retry-After 用
|
|
56
58
|
throw e;
|
package/src/web/app.js
CHANGED
|
@@ -64,6 +64,7 @@ function initTips() {
|
|
|
64
64
|
const perm = $('#permSel'); if (perm) attachTip(perm, perm.getAttribute('title'));
|
|
65
65
|
const reas = $('#reasoningSel'); if (reas) attachTip(reas, reas.getAttribute('title'));
|
|
66
66
|
const model = $('#modelSel'); if (model) attachTip(model, () => { const o = model.options[model.selectedIndex]; return (o && o.title) ? o.title : '切换模型'; });
|
|
67
|
+
const preset = $('#presetSel'); if (preset) attachTip(preset, () => { const o = preset.options[preset.selectedIndex]; return (o && o.title) ? o.title : '智能体预设:一键切换工具白名单/权限/参数组合(选「无预设」恢复默认)'; });
|
|
67
68
|
const at = $('#attachBtn'); if (at) attachTip(at, at.getAttribute('title'));
|
|
68
69
|
}
|
|
69
70
|
initTips();
|
|
@@ -148,6 +149,7 @@ $('#dirPickOk').onclick = () => { const cb = pickerCb; pickerCb = null; $('#dirM
|
|
|
148
149
|
$('#dirPickNone').onclick = () => { const cb = pickerCb; pickerCb = null; $('#dirModal').style.display = 'none'; if (cb) cb(null); };
|
|
149
150
|
$('#dirPickCancel').onclick = () => { pickerCb = null; $('#dirModal').style.display = 'none'; };
|
|
150
151
|
const chatEl = $('#chat'), input = $('#input'), sendBtn = $('#sendBtn');
|
|
152
|
+
let presetData = []; // v0.4.0 Agent Preset:预设列表缓存(含 description/tools/permission,供下拉提示与选中反馈)
|
|
151
153
|
let activeAiMsg=null, bgRunning=0, curSteps=0, curWorkT0=0, curTools=0, curTasks=0; // 本轮进度(活动条/状态条/轨迹共用)
|
|
152
154
|
let bgTasks=[]; // 后台任务列表快照(chip tooltip 详情用,updateTasksPanel 每 2s 刷新)
|
|
153
155
|
let curPhase='模型推理中'; // 阶段语义(服务端 progress 事件下发)
|
|
@@ -615,6 +617,23 @@ function applyReasoningUI(reasoning){
|
|
|
615
617
|
sel.value = ['off','low','high','max'].includes(effort) ? effort : 'high';
|
|
616
618
|
}
|
|
617
619
|
$('#reasoningSel').onchange=()=>{ applyConfig({reasoningEffort:$('#reasoningSel').value}); };
|
|
620
|
+
// v0.4.0 Agent Preset:选中预设时给出「这是什么 / 覆盖了什么」的即时反馈(否则用户不知道各选项作用)
|
|
621
|
+
$('#presetSel').onchange=()=>{ presetPicked(); };
|
|
622
|
+
function presetPicked(){
|
|
623
|
+
const val=$('#presetSel')?.value||'';
|
|
624
|
+
const p=presetData.find((x)=>x.name===val);
|
|
625
|
+
if(!p){ renderBanner({ text: '已取消预设,恢复默认配置(不注入预设定制段/白名单/权限覆盖)。' }); return; } // 选回「无预设」= 退出预设模式
|
|
626
|
+
const over=[];
|
|
627
|
+
if(Array.isArray(p.tools)) over.push('工具白名单 '+p.tools.length+' 个');
|
|
628
|
+
if(p.permission) over.push('权限 '+p.permission);
|
|
629
|
+
if(p.model) over.push('模型 '+p.model);
|
|
630
|
+
if(p.maxRounds) over.push('maxRounds '+p.maxRounds);
|
|
631
|
+
if(p.maxOutputTokens) over.push('maxOutput '+p.maxOutputTokens);
|
|
632
|
+
if(p.temperature!==undefined) over.push('温度 '+p.temperature);
|
|
633
|
+
if(p.contextBudget) over.push('预算 '+p.contextBudget);
|
|
634
|
+
const summary=over.length?'(覆盖:'+over.join(' · ')+')':'';
|
|
635
|
+
renderBanner({ text: '🧩 已选预设「'+(p.label||p.name)+'」:'+(p.description||'(无描述)')+summary+'。随本次发送生效,其余设置保持不变。' });
|
|
636
|
+
}
|
|
618
637
|
async function init(){
|
|
619
638
|
try{
|
|
620
639
|
const r=await fetch('/api/state',{cache:'no-store'}); const j=await r.json();
|
|
@@ -653,7 +672,8 @@ async function init(){
|
|
|
653
672
|
try{
|
|
654
673
|
// v0.4.0 Agent Preset:加载预设列表进下拉(项目 → 用户 → 内置)
|
|
655
674
|
const pr=await fetch('/api/presets',{cache:'no-store'}).catch(()=>null); const pj=pr?await pr.json():{presets:[]};
|
|
656
|
-
|
|
675
|
+
presetData = Array.isArray(pj.presets) ? pj.presets : [];
|
|
676
|
+
const psel=$('#presetSel'); if(psel&&presetData.length){ for(const p of presetData){ const o=document.createElement('option'); o.value=p.name; o.textContent=p.label+'('+(p.source==='project'?'项目':p.source==='user'?'用户':'内置')+')'; o.title=p.description||p.name; psel.appendChild(o); } }
|
|
657
677
|
}catch(e){}
|
|
658
678
|
try{
|
|
659
679
|
const dr=await fetch('/api/draft?file='+encodeURIComponent(currentSession||''),{cache:'no-store'}); const dj=await dr.json();
|
package/src/web/index.html
CHANGED
|
@@ -332,7 +332,7 @@ footer{flex:none;border-top:1px solid var(--border);background:var(--bg2);paddin
|
|
|
332
332
|
<option value="readonly">只读</option>
|
|
333
333
|
</select>
|
|
334
334
|
<select id="modelSel" title="切换模型"></select>
|
|
335
|
-
<select id="presetSel" title="
|
|
335
|
+
<select id="presetSel" title="智能体预设:一键切换工具白名单/权限/参数组合(选「无预设」恢复默认)"><option value="">无预设</option></select>
|
|
336
336
|
<select id="reasoningSel" title="思考模式(推理等级):关=不推理省 token · 低 · 高(默认)· 最高=最强推理" style="display:none">
|
|
337
337
|
<option value="off">关</option>
|
|
338
338
|
<option value="low">低</option>
|