@iamsamyiok/agents-chat 3.19.0 → 3.19.1
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/app/lib/cards.js +11 -5
- package/app/public/cards.html +7 -3
- package/app/public/index.html +1 -1
- package/app/server.js +14 -12
- package/package.json +1 -1
package/app/lib/cards.js
CHANGED
|
@@ -227,6 +227,8 @@ class CardRunner {
|
|
|
227
227
|
this.running = false;
|
|
228
228
|
this.timer = null;
|
|
229
229
|
this.procs = new Map(); // cardId -> { pid, child, lastActive, status }
|
|
230
|
+
this.followups = new Set(); // 追加聊天中的卡牌(并发防护)
|
|
231
|
+
this.baseline = null; // 本轮编排开始时的 done/failed 基线(all_done 报增量)
|
|
230
232
|
}
|
|
231
233
|
isRunning() { return this.running; }
|
|
232
234
|
|
|
@@ -271,6 +273,9 @@ class CardRunner {
|
|
|
271
273
|
if (this.running) return;
|
|
272
274
|
this.running = true;
|
|
273
275
|
this.token++;
|
|
276
|
+
// 记录基线:all_done 时报告本轮增量(成功/失败数),避免混入历史任务
|
|
277
|
+
const all0 = CardStore.list();
|
|
278
|
+
this.baseline = { done: all0.filter(c => c.status === 'done').length, failed: all0.filter(c => c.status === 'failed').length };
|
|
274
279
|
broadcast({ type: 'runner_started' });
|
|
275
280
|
this.tick();
|
|
276
281
|
}
|
|
@@ -283,9 +288,11 @@ class CardRunner {
|
|
|
283
288
|
if (!eligible.length) {
|
|
284
289
|
if (this.active.size === 0) {
|
|
285
290
|
this.running = false;
|
|
286
|
-
//
|
|
287
|
-
const
|
|
288
|
-
const
|
|
291
|
+
// 完成通知附本轮增量统计(成功/失败数),前端据此提醒
|
|
292
|
+
const all = CardStore.list();
|
|
293
|
+
const done = Math.max(0, all.filter(c => c.status === 'done').length - (this.baseline ? this.baseline.done : 0));
|
|
294
|
+
const failed = Math.max(0, all.filter(c => c.status === 'failed').length - (this.baseline ? this.baseline.failed : 0));
|
|
295
|
+
this.baseline = null;
|
|
289
296
|
broadcast({ type: 'all_done', done, failed });
|
|
290
297
|
}
|
|
291
298
|
return;
|
|
@@ -421,8 +428,7 @@ class CardRunner {
|
|
|
421
428
|
const card = CardStore.get(cardId);
|
|
422
429
|
if (!card) return { ok: false, error: '任务不存在' };
|
|
423
430
|
if (card.status === 'running' || card.status === 'pending') return { ok: false, error: '任务尚未执行完成,先运行任务再追加聊天' };
|
|
424
|
-
if (this.followups
|
|
425
|
-
if (!this.followups) this.followups = new Set();
|
|
431
|
+
if (this.followups.has(cardId)) return { ok: false, error: '该任务已有追加聊天进行中' };
|
|
426
432
|
this.followups.add(cardId);
|
|
427
433
|
|
|
428
434
|
const runner = resolveRunner();
|
package/app/public/cards.html
CHANGED
|
@@ -540,6 +540,8 @@ async function openDetail(id){
|
|
|
540
540
|
if(!msgs.length) log.innerHTML='<span class="empty">暂无过程记录</span>';
|
|
541
541
|
for(const m of msgs){
|
|
542
542
|
if(m.role==='user') appendLog(log,'👤 '+m.content,'msg-user');
|
|
543
|
+
else if(m.phase==='archive') appendLog(log,'📦 '+m.content,'msg-tool');
|
|
544
|
+
else if(m.phase==='system') appendLog(log,'⚠ '+m.content,'msg-err');
|
|
543
545
|
else if(/工具|执行完成/.test(m.content||'')) appendLog(log,'🔧 '+m.content,'msg-tool');
|
|
544
546
|
else if(/出错|失败/.test(m.content||'')) appendLog(log,'⚠ '+m.content,'msg-err');
|
|
545
547
|
else appendLog(log,'🤖 '+(m.content||'').slice(0,4000),'msg-assistant');
|
|
@@ -586,7 +588,7 @@ let G_SIG=''; // 结构+状态指纹:无变化时跳过重绘(避免流式
|
|
|
586
588
|
function renderGraph(force){
|
|
587
589
|
const box=$('#graphBody');
|
|
588
590
|
const cards=CARDS;
|
|
589
|
-
const sig=cards.map(c=>c.id+':'+c.status+':'+(c.dependsOn||[]).join(',')).join('|');
|
|
591
|
+
const sig=cards.map(c=>c.id+':'+c.status+':'+(c.dependsOn||[]).join(',')+':'+c.title+':'+c.mode).join('|');
|
|
590
592
|
if(!force && sig===G_SIG && box.querySelector('svg')) return;
|
|
591
593
|
G_SIG=sig;
|
|
592
594
|
if(!cards.length){ box.innerHTML='<div class="empty" style="padding:24px 8px">暂无任务<br/>新建任务后此处展示依赖关系</div>'; return; }
|
|
@@ -764,17 +766,19 @@ es.onmessage = (e)=>{
|
|
|
764
766
|
}
|
|
765
767
|
if(ev.type==='ws_warning'){ toast('⚠ 工作区路径无效:'+ev.path+',任务将在默认目录执行'); }
|
|
766
768
|
if(ev.type==='followup_start'||ev.type==='followup_done'){ loadCards(); }
|
|
767
|
-
|
|
769
|
+
// text 为正文快照(不改变卡片状态):仅实时渲染,不触发全量刷新,避免流式期间频繁重建 DOM
|
|
770
|
+
if(['task_start','tool','task_done','proc'].includes(ev.type)){
|
|
768
771
|
loadCards();
|
|
769
772
|
refreshProcs();
|
|
770
773
|
if(OPEN_ID && ev.cardId===OPEN_ID){
|
|
771
774
|
const log=$('#dLog'); if(log.querySelector('.empty')) log.innerHTML='';
|
|
772
775
|
if(ev.type==='task_start') TEXT_PARTS.clear();
|
|
773
|
-
if(ev.type==='text') renderSnapText(ev);
|
|
774
776
|
else if(ev.type==='tool'){ appendLog(log,'🔧 ['+ev.name+'] '+(ev.summary||''),'msg-tool'); log.scrollTop=log.scrollHeight; }
|
|
775
777
|
else if(ev.type==='task_done'){ loadCards().then(()=>openDetail(OPEN_ID)); }
|
|
776
778
|
}
|
|
777
779
|
}
|
|
780
|
+
// 正文快照独立渲染(不依赖上面的状态事件)
|
|
781
|
+
if(ev.type==='text' && OPEN_ID===ev.cardId) renderSnapText(ev);
|
|
778
782
|
if(ev.type==='followup_start' && OPEN_ID===ev.cardId) TEXT_PARTS.clear();
|
|
779
783
|
if(ev.type==='followup_done' && OPEN_ID===ev.cardId){
|
|
780
784
|
fuSetState(false);
|
package/app/public/index.html
CHANGED
|
@@ -634,7 +634,7 @@
|
|
|
634
634
|
</div>
|
|
635
635
|
|
|
636
636
|
<script>
|
|
637
|
-
const PAGE_VERSION = '3.19.
|
|
637
|
+
const PAGE_VERSION = '3.19.1'; // 与服务端 /api/health.version 互检,不一致说明页面缓存过期
|
|
638
638
|
const AV_COLORS = ['#5b8def','#07c160','#fa9d3b','#10aeff','#8a6fe8','#fa5151','#e8a33d','#3aa7a3'];
|
|
639
639
|
const PHASE_LABEL = { plan: '调度规划', work: '执行', review: '验收', report: '汇总', talk: '圆桌发言', task: '任务执行' };
|
|
640
640
|
// 职业图标库(智能体配置可选)
|
package/app/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Agents Chat Portable - 零依赖 HTTP 服务
|
|
2
2
|
// 启动:node app/server.js [--port 3456]
|
|
3
|
-
const APP_VERSION = '3.19.
|
|
3
|
+
const APP_VERSION = '3.19.1'; // 页面与服务端版本互检,不一致提示强刷
|
|
4
4
|
const http = require('http');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
@@ -968,22 +968,24 @@ const server = http.createServer(async (req, res) => {
|
|
|
968
968
|
}
|
|
969
969
|
if (p === '/api/cards/config' && req.method === 'POST') {
|
|
970
970
|
const body = await readBody(req);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const ws = (cfg.workspace || '').trim();
|
|
975
|
-
if (body.workspace !== undefined && ws) {
|
|
976
|
-
try { if (!fs.existsSync(ws) || !fs.statSync(ws).isDirectory()) warning = `工作区路径当前不存在或不是目录:${ws},任务执行时将回退到默认目录`; } catch { warning = `工作区路径无法访问:${ws}`; }
|
|
977
|
-
}
|
|
978
|
-
// 并行度(1-8):热更新,调度器每轮 tick 动态读取
|
|
971
|
+
// 按 patch 语义合并:仅写入请求中显式出现的字段(避免单项更新时清空另一项)
|
|
972
|
+
const patch = {};
|
|
973
|
+
if (body.workspace !== undefined) patch.workspace = String(body.workspace || '').trim().slice(0, 500);
|
|
979
974
|
let parallelBad = false;
|
|
980
975
|
if (body.maxParallel !== undefined) {
|
|
981
976
|
const n = Number(body.maxParallel);
|
|
982
|
-
if (n >= 1 && n <= 8)
|
|
977
|
+
if (n >= 1 && n <= 8) patch.maxParallel = Math.floor(n);
|
|
983
978
|
else parallelBad = true;
|
|
984
979
|
}
|
|
985
980
|
if (parallelBad) { json(res, 400, { success: false, error: 'maxParallel 需在 1-8 之间' }); return; }
|
|
986
|
-
|
|
981
|
+
const cfg = Object.keys(patch).length ? CardStore.setConfig(patch) : CardStore.getConfig();
|
|
982
|
+
// 工作区校验:保存允许(可能是尚未创建的目录),但路径不存在时显式警告
|
|
983
|
+
let warning = '';
|
|
984
|
+
const ws = (cfg.workspace || '').trim();
|
|
985
|
+
if (patch.workspace !== undefined && ws) {
|
|
986
|
+
try { if (!fs.existsSync(ws) || !fs.statSync(ws).isDirectory()) warning = `工作区路径当前不存在或不是目录:${ws},任务执行时将回退到默认目录`; } catch { warning = `工作区路径无法访问:${ws}`; }
|
|
987
|
+
}
|
|
988
|
+
json(res, 200, { success: true, config: cfg, warning });
|
|
987
989
|
return;
|
|
988
990
|
}
|
|
989
991
|
|
|
@@ -1152,7 +1154,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1152
1154
|
// SSE:实时推送卡牌生命周期事件(task_start/text/tool/task_done/all_done/runner_*)
|
|
1153
1155
|
const send = sse(req, res);
|
|
1154
1156
|
const unsub = sseSubscribe(send);
|
|
1155
|
-
send({ type: 'init', running: cardRunner.isRunning(), maxParallel:
|
|
1157
|
+
send({ type: 'init', running: cardRunner.isRunning(), maxParallel: cardRunner.maxP() });
|
|
1156
1158
|
req.on('close', unsub);
|
|
1157
1159
|
return;
|
|
1158
1160
|
}
|