first commit

This commit is contained in:
魏天炜
2026-11-12 15:29:12 +08:00
commit 5dabea54ab
93 changed files with 12170 additions and 0 deletions

60
demo/shared.js Normal file
View File

@@ -0,0 +1,60 @@
// 三个角色共用的小工具:trace 记录、Session 规则。
// trace 是这次演示的“证据”:每个进程把自己做了什么追加到 TRACE_DIR/<角色>.jsonl。
const fs = require('node:fs');
const path = require('node:path');
function tracer(role) {
const dir = process.env.TRACE_DIR;
const file = dir ? path.join(dir, `${role}.jsonl`) : null;
return (kind, data = {}) => {
const rec = { t: Date.now(), role, pid: process.pid, host: process.env.HOST_LABEL || role, kind, ...data };
if (file) fs.appendFileSync(file, JSON.stringify(rec) + '\n');
return rec;
};
}
// 只看事件日志,判断这个 Session 下一步要做什么。
// 云端 Worker 和调度器都用它:Worker 不需要记住任何东西,日志就是全部状态。
// { kind: 'idle' } 没事可做
// { kind: 'dispatch', calls: [...] } 模型要的工具调用还没有结果,要发给设备
// { kind: 'model' } 有新输入(用户消息 / 工具结果 / 后台完成),该调模型了
function pendingWork(events) {
let lastModel = -1;
for (let i = events.length - 1; i >= 0; i--) {
if (events[i].type === 'model_response') { lastModel = i; break; }
}
if (lastModel >= 0) {
const calls = events[lastModel].content.filter((b) => b.type === 'tool_use');
const done = new Set(events.filter((e) => e.type === 'tool_result').map((e) => e.call_id));
const open = calls.filter((c) => !done.has(c.id));
if (open.length) return { kind: 'dispatch', calls: open };
}
const fresh = events.slice(lastModel + 1).some((e) => ['user_message', 'tool_result', 'job_finished'].includes(e.type));
return fresh ? { kind: 'model' } : { kind: 'idle' };
}
// 把事件日志翻译成模型 API 的 messages。每次调模型都从头重建,所以换一个进程来跑结果完全一样。
function rebuildMessages(events) {
const msgs = [];
const pushUser = (block) => {
const last = msgs[msgs.length - 1];
if (last && last.role === 'user') last.content.push(block);
else msgs.push({ role: 'user', content: [block] });
};
for (const e of events) {
if (e.type === 'user_message') pushUser({ type: 'text', text: e.text });
else if (e.type === 'model_response') msgs.push({ role: 'assistant', content: e.content });
else if (e.type === 'tool_result') {
const block = { type: 'tool_result', tool_use_id: e.call_id, content: e.output };
const last = msgs[msgs.length - 1];
// tool_result 必须紧跟在对应的 assistant 之后,排在同一轮里其它输入的前面
if (last && last.role === 'user') last.content.unshift(block);
else msgs.push({ role: 'user', content: [block] });
} else if (e.type === 'job_finished') {
pushUser({ type: 'text', text: `[后台任务完成] ${e.job_id}:\`${e.command}\` 退出码 ${e.exit_code}\n输出末尾:\n${e.tail}` });
}
}
return msgs;
}
module.exports = { tracer, pendingWork, rebuildMessages };