@iamsamyiok/agents-chat 3.19.1 → 3.19.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/app/lib/cards.js +20 -6
- package/app/public/cards.html +11 -4
- package/app/public/index.html +1 -1
- package/app/server.js +4 -4
- package/package.json +1 -1
package/app/lib/cards.js
CHANGED
|
@@ -356,6 +356,9 @@ class CardRunner {
|
|
|
356
356
|
let sesId = ocSessionId;
|
|
357
357
|
|
|
358
358
|
let child = null;
|
|
359
|
+
// 进程登记:spawn 前占位(执行中即可见于进程条),spawn 后回填真实 PID
|
|
360
|
+
this.procs.set(card.id, { pid: null, child: null, lastActive: Date.now() });
|
|
361
|
+
broadcast({ type: 'proc', cardId: card.id, pid: null });
|
|
359
362
|
try {
|
|
360
363
|
await new Promise((resolve) => {
|
|
361
364
|
child = oc.chatSolo(kind, runner, {
|
|
@@ -385,15 +388,15 @@ class CardRunner {
|
|
|
385
388
|
resolve();
|
|
386
389
|
}
|
|
387
390
|
});
|
|
391
|
+
// chatSolo 同步返回 child(spawn 已完成):立即回填真实 PID,执行中即可见于进程条
|
|
392
|
+
const proc0 = this.procs.get(card.id);
|
|
393
|
+
if (proc0 && child) { proc0.pid = child.pid; proc0.child = child; }
|
|
394
|
+
broadcast({ type: 'proc', cardId: card.id, pid: child ? child.pid : null });
|
|
388
395
|
});
|
|
389
396
|
} catch (err) {
|
|
390
397
|
doneError = String((err && err.message) || err).slice(0, 2000);
|
|
391
398
|
}
|
|
392
399
|
|
|
393
|
-
// 登记进程信息(PID + 是否工作中),并广播给前端
|
|
394
|
-
this.procs.set(card.id, { pid: child ? child.pid : null, child, lastActive: Date.now() });
|
|
395
|
-
broadcast({ type: 'proc', cardId: card.id, pid: child ? child.pid : null });
|
|
396
|
-
|
|
397
400
|
const finalText = order.map(id => texts.get(id)).join('\n\n').trim();
|
|
398
401
|
const stopped = myToken !== this.token;
|
|
399
402
|
if (finalText) {
|
|
@@ -403,8 +406,9 @@ class CardRunner {
|
|
|
403
406
|
CardStore.update(card.id, {
|
|
404
407
|
status,
|
|
405
408
|
ocSessionId: sesId,
|
|
406
|
-
|
|
407
|
-
|
|
409
|
+
// 手动停止回到待执行:清掉残留,避免 pending 卡带着脏结果/错误
|
|
410
|
+
result: stopped ? '' : (doneError ? `执行出错:${doneError}` : finalText).slice(0, 20000),
|
|
411
|
+
error: stopped ? '' : (doneError || ''),
|
|
408
412
|
finishedAt: Date.now()
|
|
409
413
|
});
|
|
410
414
|
broadcast({ type: 'task_done', cardId: card.id, status, title: card.title });
|
|
@@ -415,6 +419,8 @@ class CardRunner {
|
|
|
415
419
|
async runOne(cardId) {
|
|
416
420
|
const card = CardStore.get(cardId);
|
|
417
421
|
if (!card || card.status === 'running') return false;
|
|
422
|
+
// 并发防护:追加聊天进行中的卡不可同时执行(避免同一会话被两条链路并发续写)
|
|
423
|
+
if (this.followups.has(cardId)) return false;
|
|
418
424
|
this.active.add(cardId);
|
|
419
425
|
const myToken = this.token;
|
|
420
426
|
await this.runCard(card, myToken).catch(() => {});
|
|
@@ -428,6 +434,7 @@ class CardRunner {
|
|
|
428
434
|
const card = CardStore.get(cardId);
|
|
429
435
|
if (!card) return { ok: false, error: '任务不存在' };
|
|
430
436
|
if (card.status === 'running' || card.status === 'pending') return { ok: false, error: '任务尚未执行完成,先运行任务再追加聊天' };
|
|
437
|
+
if (this.active.has(cardId)) return { ok: false, error: '任务正在执行中,稍后再追加聊天' };
|
|
431
438
|
if (this.followups.has(cardId)) return { ok: false, error: '该任务已有追加聊天进行中' };
|
|
432
439
|
this.followups.add(cardId);
|
|
433
440
|
|
|
@@ -449,6 +456,8 @@ class CardRunner {
|
|
|
449
456
|
if (!userText) return { ok: false, error: '请输入追加内容' };
|
|
450
457
|
store.addMessage({ role: 'user', agentId: 'solo', agentName: '我', actor: 'user', phase: 'followup', taskId: cardId, content: userText.slice(0, 20000) });
|
|
451
458
|
broadcast({ type: 'followup_start', cardId: cardId });
|
|
459
|
+
// 进程登记:spawn 前占位,spawn 后回填 PID(追加聊天执行中亦可见于进程条)
|
|
460
|
+
this.procs.set(cardId, { pid: null, child: null, lastActive: Date.now() });
|
|
452
461
|
|
|
453
462
|
const texts = new Map();
|
|
454
463
|
const order = [];
|
|
@@ -481,9 +490,14 @@ class CardRunner {
|
|
|
481
490
|
resolve();
|
|
482
491
|
}
|
|
483
492
|
});
|
|
493
|
+
const proc0 = this.procs.get(cardId);
|
|
494
|
+
if (proc0 && child) { proc0.pid = child.pid; proc0.child = child; }
|
|
495
|
+
broadcast({ type: 'proc', cardId, pid: child ? child.pid : null });
|
|
484
496
|
});
|
|
485
497
|
} catch (err) {
|
|
486
498
|
doneError = String((err && err.message) || err).slice(0, 2000);
|
|
499
|
+
} finally {
|
|
500
|
+
this.procs.delete(cardId);
|
|
487
501
|
}
|
|
488
502
|
|
|
489
503
|
const finalText = order.map(id => texts.get(id)).join('\n\n').trim();
|
package/app/public/cards.html
CHANGED
|
@@ -525,8 +525,15 @@ function fuSetState(running, tipText){
|
|
|
525
525
|
btn.textContent = running ? '回复中…' : '发送';
|
|
526
526
|
if(tipText!==undefined) $('#fuTip').textContent = tipText;
|
|
527
527
|
}
|
|
528
|
+
let DETAIL_SEQ = 0; // 详情加载序号:快速切换任务时只认最新请求(响应乱序防护)
|
|
528
529
|
async function openDetail(id){
|
|
529
|
-
|
|
530
|
+
if(!id) return;
|
|
531
|
+
const seq = ++DETAIL_SEQ;
|
|
532
|
+
OPEN_ID = id;
|
|
533
|
+
const data = await api('/api/cards/'+encodeURIComponent(id)+'/log');
|
|
534
|
+
if (seq !== DETAIL_SEQ || OPEN_ID !== id) return; // 已切换到其他任务或关闭弹窗:丢弃过期响应
|
|
535
|
+
if(!data || !data.success || !data.card){ toast('任务不存在或已被删除'); return; }
|
|
536
|
+
const card=data.card; const msgs=data.messages||[];
|
|
530
537
|
$('#dTitle').textContent=card.title;
|
|
531
538
|
$('#dMeta').innerHTML=badge(card)+`<span class="badge">${card.status}</span>`;
|
|
532
539
|
const depTitles = (card.dependsOn||[]).map(d=>{const c=CARDS.find(x=>x.id===d);return c?c.title:d;});
|
|
@@ -560,7 +567,7 @@ async function openDetail(id){
|
|
|
560
567
|
fuSetState(FU_RUNNING);
|
|
561
568
|
$('#maskDetail').classList.add('show');
|
|
562
569
|
}
|
|
563
|
-
$('#dClose').onclick=()=>{ OPEN_ID=null; $('#maskDetail').classList.remove('show'); };
|
|
570
|
+
$('#dClose').onclick=()=>{ OPEN_ID=null; DETAIL_SEQ++; $('#maskDetail').classList.remove('show'); };
|
|
564
571
|
$('#dRun').onclick=async()=>{ if(!OPEN_ID)return; await runOne(OPEN_ID); openDetail(OPEN_ID); };
|
|
565
572
|
|
|
566
573
|
// ---- 追加聊天:复用会话第二轮输入 ----
|
|
@@ -774,7 +781,7 @@ es.onmessage = (e)=>{
|
|
|
774
781
|
const log=$('#dLog'); if(log.querySelector('.empty')) log.innerHTML='';
|
|
775
782
|
if(ev.type==='task_start') TEXT_PARTS.clear();
|
|
776
783
|
else if(ev.type==='tool'){ appendLog(log,'🔧 ['+ev.name+'] '+(ev.summary||''),'msg-tool'); log.scrollTop=log.scrollHeight; }
|
|
777
|
-
else if(ev.type==='task_done'){ loadCards().then(()=>
|
|
784
|
+
else if(ev.type==='task_done'){ loadCards().then(()=>{ if(OPEN_ID===ev.cardId) openDetail(ev.cardId); }); }
|
|
778
785
|
}
|
|
779
786
|
}
|
|
780
787
|
// 正文快照独立渲染(不依赖上面的状态事件)
|
|
@@ -783,7 +790,7 @@ es.onmessage = (e)=>{
|
|
|
783
790
|
if(ev.type==='followup_done' && OPEN_ID===ev.cardId){
|
|
784
791
|
fuSetState(false);
|
|
785
792
|
if(ev.error){ appendLog($('#dLog'),'⚠ '+ev.error,'msg-err'); toast('追加聊天失败:'+errSummary(ev.error)); }
|
|
786
|
-
loadCards().then(()=>
|
|
793
|
+
loadCards().then(()=>{ if(OPEN_ID===ev.cardId) openDetail(ev.cardId); });
|
|
787
794
|
}
|
|
788
795
|
};
|
|
789
796
|
es.onerror=()=>{};
|
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.2'; // 与服务端 /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.2'; // 页面与服务端版本互检,不一致提示强刷
|
|
4
4
|
const http = require('http');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
@@ -1111,9 +1111,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
1111
1111
|
if (body.title !== undefined) patch.title = String(body.title).slice(0, 500);
|
|
1112
1112
|
if (body.content !== undefined) patch.content = String(body.content).slice(0, 20000);
|
|
1113
1113
|
if (body.priority !== undefined) patch.priority = Number(body.priority) || 999;
|
|
1114
|
-
if (body.mode !== undefined) patch.mode = body.mode;
|
|
1115
|
-
if (body.chainId !== undefined) patch.chainId = body.chainId;
|
|
1116
|
-
if (Array.isArray(body.dependsOn)) patch.dependsOn = body.dependsOn.map(String);
|
|
1114
|
+
if (body.mode !== undefined) patch.mode = ['new', 'continue', 'parallel'].includes(body.mode) ? body.mode : 'new';
|
|
1115
|
+
if (body.chainId !== undefined) patch.chainId = String(body.chainId).slice(0, 100);
|
|
1116
|
+
if (Array.isArray(body.dependsOn)) patch.dependsOn = body.dependsOn.map(String).slice(0, 50);
|
|
1117
1117
|
if (body.model !== undefined) patch.model = String(body.model).slice(0, 80);
|
|
1118
1118
|
// 状态手动复位:failed/pending -> pending 可重跑
|
|
1119
1119
|
if (body.status === 'pending') { patch.status = 'pending'; patch.result = ''; patch.error = ''; patch.ocSessionId = ''; }
|