deepccc 0.2.29 → 0.2.31

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/README.md CHANGED
@@ -197,6 +197,9 @@ Provider 发送 `max_tokens`,使用模型服务端默认值。可通过 `DEEPC
197
197
  API 拒绝(context length exceeded),实际窗口以模型与所用服务端为准(如 litellm 的
198
198
  `max_input_tokens`)。
199
199
 
200
+ 同一轮连续工具调用也有独立的结果重放预算。较早的简短成功回执、JSON 结果和错误信息,
201
+ 若比省略提示更短,会保留原结果;较大的结果仍按预算截断或省略,工具调用关联保持不变。
202
+
200
203
  `subModel` 是子模型(选填),默认 `""`(留空跟随主模型)。配置后,DeepCCC 内部的轻量
201
204
  环节——上下文压缩摘要生成、`task` 子代理任务——使用子模型执行,主对话仍用主模型。
202
205
  典型用法:主模型用 pro 承担复杂推理,子模型用 flash 做高频廉价的摘要与子任务。
@@ -460,6 +463,8 @@ deepccc 的内核主战场在 ChatCCC 仓库的 `deepccc-agent/` 子目录;本
460
463
 
461
464
  三个命令工具都支持独立 `cwd`、超时、取消、输出限长和运行中消息让位;返回非零退出码时不会直接被当成工具异常,模型可以读取结构化结果继续判断。Windows 下 `run_command` 会拒绝多行或嵌套同类引号的复杂 Python/Node 内联代码,并提示改用 `run_script`。
462
465
 
466
+ 命令工具及后台命令查询/终止会先检查必需的命名参数。误把参数放进 `raw`、传 JSON 字符串或缺少必需字段时,会在权限处理与进程操作前返回明确的入参错误;有效脚本里的 JSON 内容保持原样,不会被当作参数重新解析。
467
+
463
468
  ## License
464
469
 
465
470
  Apache-2.0
@@ -1474,6 +1474,15 @@ export function killAllBackgroundTasks() {
1474
1474
  }
1475
1475
  backgroundTasks.clear();
1476
1476
  }
1477
+ function requireCommandArguments(name, input, requiredStrings, allowEmpty = []) {
1478
+ const value = input && typeof input === "object" && !Array.isArray(input)
1479
+ ? input : undefined;
1480
+ if (!value || requiredStrings.some((key) => typeof value[key] !== "string"
1481
+ || (!allowEmpty.includes(key) && !value[key].trim()))) {
1482
+ throw new Error(`Invalid ${name} arguments: expected a structured object with string fields ${requiredStrings.join(", ")}. `
1483
+ + "Pass these named fields directly; do not wrap the arguments in raw or a JSON string.");
1484
+ }
1485
+ }
1477
1486
  export function createBuiltinFileTools(cwd, options = {}) {
1478
1487
  const gate = options.permissionGate;
1479
1488
  const runTask = options.runTask;
@@ -1601,6 +1610,7 @@ export function createBuiltinFileTools(cwd, options = {}) {
1601
1610
  required: ["executable"],
1602
1611
  }),
1603
1612
  execute: async (input, execOptions) => {
1613
+ requireCommandArguments("run_process", input, ["executable"]);
1604
1614
  const action = [input.executable, ...(input.args ?? [])].join(" ");
1605
1615
  await guard({
1606
1616
  tool: "run_process",
@@ -1631,6 +1641,10 @@ export function createBuiltinFileTools(cwd, options = {}) {
1631
1641
  required: ["language", "code"],
1632
1642
  }),
1633
1643
  execute: async (input, execOptions) => {
1644
+ requireCommandArguments("run_script", input, ["language", "code"], ["code"]);
1645
+ if (input.language !== "node" && input.language !== "python") {
1646
+ throw new Error("Invalid run_script arguments: language must be node or python.");
1647
+ }
1634
1648
  const runtime = input.executable?.trim() || input.language;
1635
1649
  await guard({
1636
1650
  tool: "run_script",
@@ -1654,6 +1668,7 @@ export function createBuiltinFileTools(cwd, options = {}) {
1654
1668
  required: ["command"],
1655
1669
  }),
1656
1670
  execute: async (input, execOptions) => {
1671
+ requireCommandArguments("run_command", input, ["command"]);
1657
1672
  await guard({
1658
1673
  tool: "run_command",
1659
1674
  action: input.command,
@@ -1674,7 +1689,10 @@ export function createBuiltinFileTools(cwd, options = {}) {
1674
1689
  },
1675
1690
  required: ["taskId"],
1676
1691
  }),
1677
- execute: (input) => commandOutputForTool(input.taskId, input, options.shouldYieldToInjection),
1692
+ execute: async (input) => {
1693
+ requireCommandArguments("command_output", input, ["taskId"]);
1694
+ return commandOutputForTool(input.taskId, input, options.shouldYieldToInjection);
1695
+ },
1678
1696
  }),
1679
1697
  command_kill: tool({
1680
1698
  description: "终止一个由 run_process、run_script 或 run_command 转入后台的进程。",
@@ -1686,7 +1704,10 @@ export function createBuiltinFileTools(cwd, options = {}) {
1686
1704
  },
1687
1705
  required: ["taskId"],
1688
1706
  }),
1689
- execute: (input) => commandKillForTool(input.taskId),
1707
+ execute: async (input) => {
1708
+ requireCommandArguments("command_kill", input, ["taskId"]);
1709
+ return commandKillForTool(input.taskId);
1710
+ },
1690
1711
  }),
1691
1712
  task: tool({
1692
1713
  description: `把独立子任务委派给子代理执行:子代理使用子模型、拥有独立上下文,不污染主对话上下文。可用 maxSteps 指定 ${MIN_TASK_MAX_STEPS}-${MAX_TASK_MAX_STEPS} 步预算。默认同步等待;runInBackground=true 时立即返回 taskId,之后用 task_output 查看或 task_stop 要求其停止调查并总结。子代理不能再次委派任务(禁止嵌套),结果会截断回传。`,
@@ -72,7 +72,9 @@ export function compactToolLoopMessages(input, options = {}) {
72
72
  for (let index = prepared.length - keepRecent - 1; index >= 0; index -= 1) {
73
73
  const item = prepared[index];
74
74
  const delta = item.cappedSize - omittedSize;
75
- if (delta <= 0 || retainedToolChars + delta > maximum)
75
+ // A short status/error can cost less than the omission marker. Always
76
+ // restore such results: this retains evidence and reduces replay size.
77
+ if (delta > 0 && retainedToolChars + delta > maximum)
76
78
  continue;
77
79
  item.message.content[item.partIndex] = { ...item.part, output: item.capped };
78
80
  retainedToolChars += delta;
package/dist/web-page.js CHANGED
@@ -36,11 +36,11 @@ export const DEEPCCC_WEB_PAGE = `<!DOCTYPE html>
36
36
  <section id="content" class="content"><div class="welcome"><div class="welcome-card"><div class="welcome-mark">✦</div><h2>把工作交给 DeepCCC</h2><p>在浏览器里管理多个本地 Coding Agent 会话,切换模型与推理强度,并在会话内审批操作。</p><div class="hint-grid"><div class="hint">多会话并行运行</div><div class="hint">本地文件与历史</div><div class="hint">API 自由配置</div></div></div></div></section>
37
37
  </main>
38
38
  </div>
39
- <div id="composer-shell" class="composer-shell hidden"><div id="composer" class="composer"><textarea id="prompt" placeholder="描述你想完成的任务…可粘贴或拖入图片"></textarea><div id="attachment-drafts" class="attachment-drafts"></div><div class="composer-bar"><input id="attachment-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif" multiple hidden><button id="attach-image" class="attach" type="button" title="添加图片">+</button><input id="session-model" list="recent-models" aria-label="模型"><input id="session-sub-model" list="recent-models" aria-label="子模型" placeholder="默认子模型"><datalist id="recent-models"></datalist><select id="session-effort" aria-label="推理强度"><option value="">默认 effort</option><option>none</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option></select><button id="send" class="send" title="发送">↑</button><button id="stop" class="stop hidden">■ 停止</button></div></div></div>
39
+ <div id="composer-shell" class="composer-shell hidden"><div id="composer" class="composer"><textarea id="prompt" placeholder="描述你想完成的任务…可粘贴或拖入图片"></textarea><div id="attachment-drafts" class="attachment-drafts"></div><div class="composer-bar"><input id="attachment-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif" multiple hidden><button id="attach-image" class="attach" type="button" title="添加图片">+</button><input id="session-model" list="recent-models" aria-label="模型"><input id="session-sub-model" list="recent-models" aria-label="子模型" placeholder="默认子模型"><datalist id="recent-models"></datalist><select id="session-effort" aria-label="推理强度"><option value="">默认 effort</option><option>none</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option></select><button id="send" class="send" title="发送">↑</button><button id="stop" class="stop hidden">■ 停止</button></div></div></div>
40
40
 
41
41
  <div id="new-modal" class="modal-backdrop hidden"><form id="new-form" class="modal"><h2>新建 Agent 会话</h2><p class="subtitle">每个会话拥有独立上下文;同目录会话可以并发,但文件修改可能冲突。</p><div class="field"><label>工作目录</label><input id="new-cwd" required></div><div class="field"><label>会话名称(可选)</label><input id="new-title" placeholder="默认使用目录名"></div><div class="row"><div class="field"><label>模型</label><input id="new-model" list="recent-models"></div><div class="field"><label>子模型(可选)</label><input id="new-sub-model" list="recent-models"></div></div><div class="field"><label>Effort</label><select id="new-effort"><option value="">默认</option><option>none</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option></select></div><div class="modal-actions"><button type="button" data-close="new-modal" class="ghost">取消</button><button class="primary" type="submit">创建会话</button></div></form></div>
42
42
 
43
- <div id="settings-modal" class="modal-backdrop hidden"><form id="settings-form" class="modal"><h2>API 与 Web 设置</h2><p class="subtitle">单一 API 配置作为所有新会话的默认值;会话可单独覆盖模型和 effort。</p><div class="row"><div class="field"><label>API 协议</label><select id="cfg-provider"><option value="openai">OpenAI-compatible</option><option value="anthropic">Anthropic Messages</option></select></div><div class="field"><label>API Key</label><input id="cfg-api-key" type="password" placeholder="留空保留当前 Key"></div></div><div class="field"><label>Base URL</label><input id="cfg-base-url" required></div><div class="row"><div class="field"><label>默认模型</label><input id="cfg-model" required></div><div class="field"><label>子模型(可选)</label><input id="cfg-sub-model"></div></div><div class="field"><label>图片模型(选填)</label><input id="cfg-vision-model" placeholder="例如 deepseek-flash;留空使用主模型原生视觉"></div><div class="row"><div class="field"><label>默认 Effort</label><select id="cfg-effort"><option value="">不传</option><option>none</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option></select></div><div class="field"><label>Context Window</label><input id="cfg-context" type="number" min="1"></div></div><div class="row"><div class="field"><label>Web 端口</label><input id="cfg-port" type="number" min="1" max="65535"></div><div class="field"><label>Max Output Tokens(可选)</label><input id="cfg-max-output" type="number" min="1"></div></div><label class="check"><input id="cfg-streaming" type="checkbox">启用流式请求</label><label class="check"><input id="cfg-open" type="checkbox">运行 deepccc web 时自动打开浏览器</label><div id="key-state" class="subtitle"></div><div class="modal-actions"><button type="button" data-close="settings-modal" class="ghost">取消</button><button class="primary" type="submit">保存设置</button></div></form></div>
43
+ <div id="settings-modal" class="modal-backdrop hidden"><form id="settings-form" class="modal"><h2>API 与 Web 设置</h2><p class="subtitle">单一 API 配置作为所有新会话的默认值;会话可单独覆盖模型和 effort。</p><div class="row"><div class="field"><label>API 协议</label><select id="cfg-provider"><option value="openai">OpenAI-compatible</option><option value="anthropic">Anthropic Messages</option></select></div><div class="field"><label>API Key</label><input id="cfg-api-key" type="password" placeholder="留空保留当前 Key"></div></div><div class="field"><label>Base URL</label><input id="cfg-base-url" required></div><div class="row"><div class="field"><label>默认模型</label><input id="cfg-model" required></div><div class="field"><label>子模型(可选)</label><input id="cfg-sub-model"></div></div><div class="field"><label>图片模型(选填)</label><input id="cfg-vision-model" placeholder="例如 deepseek-flash;留空使用主模型原生视觉"></div><div class="row"><div class="field"><label>默认 Effort</label><select id="cfg-effort"><option value="">不传</option><option>none</option><option>minimal</option><option>low</option><option>medium</option><option>high</option><option>xhigh</option><option>max</option></select></div><div class="field"><label>Context Window</label><input id="cfg-context" type="number" min="1"></div></div><div class="row"><div class="field"><label>Web 端口</label><input id="cfg-port" type="number" min="1" max="65535"></div><div class="field"><label>Max Output Tokens(可选)</label><input id="cfg-max-output" type="number" min="1"></div></div><label class="check"><input id="cfg-streaming" type="checkbox">启用流式请求</label><label class="check"><input id="cfg-open" type="checkbox">运行 deepccc web 时自动打开浏览器</label><div id="key-state" class="subtitle"></div><div class="modal-actions"><button type="button" data-close="settings-modal" class="ghost">取消</button><button class="primary" type="submit">保存设置</button></div></form></div>
44
44
 
45
45
  <div id="rename-modal" class="modal-backdrop hidden"><form id="rename-form" class="modal"><h2>重命名会话</h2><div class="field"><label>名称</label><input id="rename-title" required maxlength="120"></div><div class="modal-actions"><button type="button" data-close="rename-modal" class="ghost">取消</button><button class="primary">保存</button></div></form></div>
46
46
 
@@ -326,7 +326,7 @@ export const DEEPCCC_WEB_PAGE = `<!DOCTYPE html>
326
326
  function clearToolSessionState(sessionId){try{var prefix='deepccc:tool-ui:v1:'+sessionId+':';for(var index=sessionStorage.length-1;index>=0;index--){var key=sessionStorage.key(index);if(key&&key.indexOf(prefix)===0)sessionStorage.removeItem(key);}}catch(error){}}
327
327
  function toolSectionHtml(call,kind,value,headLines,tailLines){if(value===undefined)return'';var result=truncateToolPayload(value,headLines,tailLines);var stateKey=call.uiKey+':'+kind+'-full';var expanded=toolState(stateKey);var text=expanded?result.full:result.preview;var label=kind==='input'?'调用参数':'工具结果';var button=result.truncated?'<button type="button" class="tool-overflow-toggle" data-tool-overflow="'+escapeHtml(stateKey)+'">'+escapeHtml(expanded?'收起省略内容':(result.omittedLines>0?'展开省略的 '+result.omittedLines+' 行':'展开完整内容'))+'</button>':'';return'<section class="tool-section"><div class="tool-section-title">'+label+'</div><pre>'+escapeHtml(text)+'</pre>'+button+'</section>';}
328
328
  function timelineHtml(timeline,messageIndex){var results={};(timeline||[]).forEach(function(item){if(item&&item.type==='tool_result'&&item.tool_use_id)results[item.tool_use_id]=item;});var renderedCalls={};var html='';(timeline||[]).forEach(function(item,index){if(!item)return;if(item.type==='text'){if(item.text)html+='<div class="markdown">'+markdown(item.text)+'</div>';return;}if(item.type==='tool_use'){var id=item.id||('timeline-'+messageIndex+'-'+index);var result=results[id];renderedCalls[id]=true;html+=toolCardHtml({uiKey:'tool:'+id,name:item.name||'tool',input:item.input,output:result?result.output:undefined,isError:!!(result&&result.is_error),pending:!result});return;}if(item.type==='tool_result'&&!renderedCalls[item.tool_use_id]){var fallbackId=item.tool_use_id||('result-'+messageIndex+'-'+index);renderedCalls[fallbackId]=true;html+=toolCardHtml({uiKey:'tool:'+fallbackId,name:item.name||'tool',output:item.output,isError:!!item.is_error,pending:false});}});return html;}
329
- function liveTimeline(events){var timeline=[];(events||[]).forEach(function(event){if(event.type!=='agent'||!event.data)return;var item=event.data;if(item.type==='image_input'){timeline.push({type:'text',text:'[图片理解:'+item.model+','+item.count+' 张,'+(item.mode==='native'?'原生视觉':'图片模型分析')+']\\n'});return;}if(item.type==='text_reset'){timeline.length=0;return;}if(item.type==='text'){var previous=timeline[timeline.length-1];if(previous&&previous.type==='text')previous.text+=(item.text||'');else timeline.push({type:'text',text:item.text||''});return;}if(item.type==='tool_use')timeline.push({type:'tool_use',id:item.id,name:item.name,input:item.input});else if(item.type==='tool_result')timeline.push({type:'tool_result',tool_use_id:item.tool_use_id,name:item.name,output:item.content,is_error:!!item.is_error});});return timeline;}
329
+ function liveTimeline(events){var timeline=[];(events||[]).forEach(function(event){if(event.type!=='agent'||!event.data)return;var item=event.data;if(item.type==='image_input'){timeline.push({type:'text',text:'[图片理解:'+item.model+','+item.count+' 张,'+(item.mode==='native'?'原生视觉':'图片模型分析')+']\\n'});return;}if(item.type==='text_reset'){timeline.length=0;return;}if(item.type==='text'){var previous=timeline[timeline.length-1];if(previous&&previous.type==='text')previous.text+=(item.text||'');else timeline.push({type:'text',text:item.text||''});return;}if(item.type==='tool_use')timeline.push({type:'tool_use',id:item.id,name:item.name,input:item.input});else if(item.type==='tool_result')timeline.push({type:'tool_result',tool_use_id:item.tool_use_id,name:item.name,output:item.content,is_error:!!item.is_error});});return timeline;}
330
330
  function legacyToolsHtml(toolCalls,messageIndex){var tools='';(toolCalls||[]).forEach(function(call,index){tools+=toolCardHtml({uiKey:call.id?'tool:'+call.id:'message:'+messageIndex+':tool:'+index,name:call.name,input:call.input,output:call.output,isError:!!call.is_error,pending:false});});return tools;}
331
331
  function messageHtml(role,content,toolCalls,messageIndex,timeline){var decoded=role==='user'?decodeAttachmentMessage(content):{text:withoutToolTranscript(content),attachments:[]};var body;if(role==='user')body='<div class="markdown">'+markdown(decoded.text)+'</div>'+attachmentGallery(decoded.attachments,state.detail.sessionId);else if(timeline&&timeline.length)body=timelineHtml(timeline,messageIndex);else body=legacyToolsHtml(toolCalls,messageIndex)+'<div class="markdown">'+markdown(decoded.text)+'</div>';return '<article class="message '+role+'"><div class="avatar">'+(role==='user'?'你':'D')+'</div><div class="bubble"><div class="role">'+(role==='user'?'YOU':'DEEPCCC')+'</div>'+body+'</div></article>';}
332
332
  function buildToolGroups(events){var groups=[];var byId={};var order=[];(events||[]).forEach(function(event){if(event.type!=='agent'||!event.data)return;var item=event.data;if(item.type==='tool_use'){var id=item.id||('call-'+order.length);var group={id:id,uiKey:'tool:'+id,name:item.name||'tool',input:item.input,output:undefined,isError:false,pending:true};byId[id]=group;order.push(id);groups.push(group);}else if(item.type==='tool_result'){var key=item.tool_use_id;var target=key?byId[key]:null;if(target){target.output=item.content;target.isError=!!item.is_error;target.pending=false;}}});return groups;}
@@ -392,12 +392,12 @@ export const DEEPCCC_WEB_PAGE = `<!DOCTYPE html>
392
392
  function scheduleSessions(){clearTimeout(state.sessionsReloadTimer);state.sessionsReloadTimer=setTimeout(function(){loadSessions().catch(function(error){toast(error.message,true);});},80);}
393
393
  function handleSessionEvent(msg){var update;try{update=JSON.parse(msg.data);}catch(error){return;}if(update.sessionId!==state.selectedId||!state.detail)return;if(update.type==='agent'||update.type==='run_started'){state.detail.events=(state.detail.events||[]).concat([update]).slice(-500);if(update.type==='run_started')state.detail.status='running';scheduleRender();}else if(update.type==='run_finished'){state.detail.events=(state.detail.events||[]).concat([update]).slice(-500);state.detail.status='idle';loadDetail(state.selectedId).catch(function(error){toast(error.message,true);});}else if(update.type==='approval'||update.type==='approval_resolved'||update.type==='session_updated'){loadDetail(state.selectedId).catch(function(error){toast(error.message,true);});}}
394
394
  async function selectSession(id){state.selectedId=id;state.detail=null;var token=++state.detailRequestToken;renderSessions();$('composer-shell').classList.add('hidden');$('content').innerHTML='<div class="welcome"><div class="welcome-card"><div class="welcome-mark">✦</div><h2>正在加载会话</h2><p>正在读取消息、工具记录与运行状态…</p></div></div>';if(state.eventSource)state.eventSource.close();state.eventSource=new EventSource('/api/sessions/'+encodeURIComponent(id)+'/events');state.eventSource.onmessage=handleSessionEvent;await loadDetail(id,token);}
395
- async function loadConfig(){var result=await api('/api/config');state.config=result.config;$('cfg-provider').value=state.config.provider;$('cfg-base-url').value=state.config.baseURL;$('cfg-model').value=state.config.model;$('cfg-sub-model').value=state.config.subModel||'';$('cfg-vision-model').value=state.config.visionModel||'';$('cfg-effort').value=state.config.effort||'';$('cfg-context').value=state.config.contextWindow;$('cfg-max-output').value=state.config.maxOutputTokens||'';$('cfg-streaming').checked=state.config.streaming!==false;$('cfg-port').value=state.config.web.port;$('cfg-open').checked=state.config.web.openOnStart!==false;$('server-port').textContent=state.config.web.port;$('key-state').textContent=state.config.apiKeyConfigured?'当前 API Key:'+state.config.apiKeyMask:'尚未配置 API Key';$('new-cwd').value=state.config.defaultCwd||'';$('new-model').value=state.config.model;$('new-sub-model').value=state.config.subModel||'';var models=[state.config.model,state.config.subModel].concat(state.sessions.reduce(function(all,item){return all.concat([item.model,item.subModel]);},[])).filter(Boolean).filter(function(value,index,array){return array.indexOf(value)===index;});$('recent-models').innerHTML=models.map(function(model){return '<option value="'+escapeHtml(model)+'">';}).join('');}
395
+ async function loadConfig(){var result=await api('/api/config');state.config=result.config;$('cfg-provider').value=state.config.provider;$('cfg-base-url').value=state.config.baseURL;$('cfg-model').value=state.config.model;$('cfg-sub-model').value=state.config.subModel||'';$('cfg-vision-model').value=state.config.visionModel||'';$('cfg-effort').value=state.config.effort||'';$('cfg-context').value=state.config.contextWindow;$('cfg-max-output').value=state.config.maxOutputTokens||'';$('cfg-streaming').checked=state.config.streaming!==false;$('cfg-port').value=state.config.web.port;$('cfg-open').checked=state.config.web.openOnStart!==false;$('server-port').textContent=state.config.web.port;$('key-state').textContent=state.config.apiKeyConfigured?'当前 API Key:'+state.config.apiKeyMask:'尚未配置 API Key';$('new-cwd').value=state.config.defaultCwd||'';$('new-model').value=state.config.model;$('new-sub-model').value=state.config.subModel||'';var models=[state.config.model,state.config.subModel].concat(state.sessions.reduce(function(all,item){return all.concat([item.model,item.subModel]);},[])).filter(Boolean).filter(function(value,index,array){return array.indexOf(value)===index;});$('recent-models').innerHTML=models.map(function(model){return '<option value="'+escapeHtml(model)+'">';}).join('');}
396
396
  $('new-session').addEventListener('click',function(){openModal('new-modal');setTimeout(function(){$('new-cwd').focus();},0);});$('open-settings').addEventListener('click',function(){loadConfig().then(function(){openModal('settings-modal');}).catch(function(error){toast(error.message,true);});});document.querySelectorAll('[data-close]').forEach(function(button){button.addEventListener('click',function(){closeModal(button.dataset.close);});});
397
397
  $('new-form').addEventListener('submit',async function(event){event.preventDefault();try{var result=await api('/api/sessions',{method:'POST',body:JSON.stringify({cwd:$('new-cwd').value,title:$('new-title').value,model:$('new-model').value,subModel:$('new-sub-model').value,effort:$('new-effort').value})});closeModal('new-modal');$('new-title').value='';await loadSessions();await selectSession(result.session.sessionId);}catch(error){toast(error.message,true);}});
398
- $('settings-form').addEventListener('submit',async function(event){event.preventDefault();var body={provider:$('cfg-provider').value,baseURL:$('cfg-base-url').value,model:$('cfg-model').value,subModel:$('cfg-sub-model').value,visionModel:$('cfg-vision-model').value,effort:$('cfg-effort').value,contextWindow:Number($('cfg-context').value),streaming:$('cfg-streaming').checked,web:{port:Number($('cfg-port').value),openOnStart:$('cfg-open').checked}};if($('cfg-max-output').value)body.maxOutputTokens=Number($('cfg-max-output').value);if($('cfg-api-key').value)body.apiKey=$('cfg-api-key').value;try{await api('/api/config',{method:'PUT',body:JSON.stringify(body)});$('cfg-api-key').value='';closeModal('settings-modal');toast('设置已保存,新配置从下一次新建会话生效');await loadConfig();}catch(error){toast(error.message,true);}});
398
+ $('settings-form').addEventListener('submit',async function(event){event.preventDefault();var body={provider:$('cfg-provider').value,baseURL:$('cfg-base-url').value,model:$('cfg-model').value,subModel:$('cfg-sub-model').value,visionModel:$('cfg-vision-model').value,effort:$('cfg-effort').value,contextWindow:Number($('cfg-context').value),streaming:$('cfg-streaming').checked,web:{port:Number($('cfg-port').value),openOnStart:$('cfg-open').checked}};if($('cfg-max-output').value)body.maxOutputTokens=Number($('cfg-max-output').value);if($('cfg-api-key').value)body.apiKey=$('cfg-api-key').value;try{await api('/api/config',{method:'PUT',body:JSON.stringify(body)});$('cfg-api-key').value='';closeModal('settings-modal');toast('设置已保存,新配置从下一次新建会话生效');await loadConfig();}catch(error){toast(error.message,true);}});
399
399
  async function uploadAttachment(sessionId,file){var response=await fetch('/api/sessions/'+encodeURIComponent(sessionId)+'/attachments?name='+encodeURIComponent(file.name||'image'),{method:'POST',headers:{'content-type':file.type||'application/octet-stream'},body:file});var result=await response.json().catch(function(){return{error:'上传响应不是有效 JSON'};});if(!response.ok)throw new Error(result.error||('HTTP '+response.status));return result.attachment;}
400
- async function handleAttachmentFiles(files){var sessionId=state.selectedId;if(!sessionId||state.uploading)return;var candidates=[].slice.call(files||[]).filter(function(file){return ['image/png','image/jpeg','image/webp','image/gif'].includes(file.type);});var existing=state.attachmentDrafts[sessionId]||[];if(existing.length+candidates.length>10){toast('每条消息最多添加 10 张图片',true);return;}if(!candidates.length){toast('仅支持 PNG、JPEG、WebP、GIF 图片',true);return;}state.uploading=true;if(state.detail)renderDetail();try{for(var i=0;i<candidates.length;i++){if(candidates[i].size>20*1024*1024)throw new Error('单张图片不能超过 20 MB:'+candidates[i].name);existing.push(await uploadAttachment(sessionId,candidates[i]));state.attachmentDrafts[sessionId]=existing;renderAttachmentDrafts();}}catch(error){toast(error.message,true);}finally{state.uploading=false;if(state.detail)renderDetail();$('attachment-input').value='';}}
400
+ async function handleAttachmentFiles(files){var sessionId=state.selectedId;if(!sessionId||state.uploading)return;var candidates=[].slice.call(files||[]).filter(function(file){return ['image/png','image/jpeg','image/webp','image/gif'].includes(file.type);});var existing=state.attachmentDrafts[sessionId]||[];if(existing.length+candidates.length>10){toast('每条消息最多添加 10 张图片',true);return;}if(!candidates.length){toast('仅支持 PNG、JPEG、WebP、GIF 图片',true);return;}state.uploading=true;if(state.detail)renderDetail();try{for(var i=0;i<candidates.length;i++){if(candidates[i].size>20*1024*1024)throw new Error('单张图片不能超过 20 MB:'+candidates[i].name);existing.push(await uploadAttachment(sessionId,candidates[i]));state.attachmentDrafts[sessionId]=existing;renderAttachmentDrafts();}}catch(error){toast(error.message,true);}finally{state.uploading=false;if(state.detail)renderDetail();$('attachment-input').value='';}}
401
401
  async function removeAttachment(id){var sessionId=state.selectedId;if(!sessionId)return;try{await api('/api/sessions/'+encodeURIComponent(sessionId)+'/attachments/'+encodeURIComponent(id),{method:'DELETE'});state.attachmentDrafts[sessionId]=(state.attachmentDrafts[sessionId]||[]).filter(function(item){return item.attachmentId!==id;});renderAttachmentDrafts();}catch(error){toast(error.message,true);}}
402
402
  async function send(){var sessionId=state.selectedId;var text=$('prompt').value;var attachments=currentAttachments();if(!sessionId||(!text.trim()&&!attachments.length)||state.uploading)return;try{var patch={model:$('session-model').value,subModel:$('session-sub-model').value,effort:$('session-effort').value};if(patch.model!==state.detail.model||patch.subModel!==state.detail.subModel||patch.effort!==state.detail.effort){var updated=await api('/api/sessions/'+encodeURIComponent(sessionId),{method:'PATCH',body:JSON.stringify(patch)});if(state.selectedId===sessionId){state.detail=Object.assign({},state.detail,updated.session);renderDetail();}await loadSessions();}await api('/api/sessions/'+encodeURIComponent(sessionId)+'/messages',{method:'POST',body:JSON.stringify({text:text,attachmentIds:attachments.map(function(item){return item.attachmentId;})})});delete state.configDrafts[sessionId];delete state.promptDrafts[sessionId];delete state.attachmentDrafts[sessionId];if(state.selectedId===sessionId){$('prompt').value='';renderAttachmentDrafts();state.detail.events=[];state.detail.status='running';renderDetail();}var token=++state.detailRequestToken;await loadDetail(sessionId,token);}catch(error){toast(error.message,true);}}
403
403
  ['session-model','session-sub-model','session-effort'].forEach(function(id){$(id).addEventListener('input',rememberDraft);$(id).addEventListener('change',rememberDraft);});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepccc",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "description": "A lightweight coding agent with OpenAI-compatible and Anthropic Messages API support.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [