New:Socket for Asana Is Now Available.Learn more
Get Started

dsh-graph

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dsh-graph - npm Package Compare versions

Comparing version
0.7.1
to
0.7.2
+1
-0
lib/client/card-drawer.js

@@ -272,2 +272,3 @@ if (blocked) {

? h(SessionPanel, { key: "live", parentId: card.parent_session_id, childId: card.child_id, collapsible: true,
provider: card.provider, model: card.model,
goalId: props.goalId, relaunchKind: "collect",

@@ -274,0 +275,0 @@ relaunchCardId: props.cardId, relaunchPrompt: promptText || autoPrompt,

@@ -70,2 +70,11 @@ const [open, setOpen] = React.useState(false);

// g-200:判断 Goal 是否有正在活跃的执行 attempt(排除收集子代理)
function hasActiveGoalExecutionAttempt(attempts) {
return (attempts ?? []).some((a) => {
if (a?.executor === "agent:collect" || a?.result !== "pending") return false;
const line = String(a?.status_line ?? "").trim();
return line !== "" && !/空闲|完成|待命|已交付|结束|等待|finished|done|idle|completed/i.test(line);
});
}
// 目标卡:只保留关键信息(标题/状态/状态行/徽标/依赖),子卡片扼要列出、点击开抽屉

@@ -253,7 +262,10 @@ // 依赖徽章状态化(发现#23):已交付依赖显示「依赖满足」,仅未交付依赖显示「等待」并触发琥珀边框

: null,
// g-a92e1406:执行会话内嵌实时条——status_line 摘要并入状态小窗
//(运行中 ⏳ / 空闲刚执行完 ✅);无执行会话时退化为独立状态行(带动画)
g.attempt_child_id
// g-a92e1406 & g-200:执行会话内嵌实时条——status_line 摘要并入状态小窗
//(运行中 ⏳ / 空闲刚执行完 ✅);当且仅当 Goal 处于执行态(in_progress)或有明确活跃执行 attempt 时展示 Goal LiveStrip;
// 仅有上下文卡片处于 collecting 时隐藏 Goal LiveStrip,避免在 Goal 与子卡片重复展示;
// 无执行会话但有 status_line 时退化为独立状态行(带动画)
g.attempt_child_id && (g.status === "in_progress" || hasActiveGoalExecutionAttempt(g.attempts))
? h("div", { key: "live" },
h(LiveStrip, { parentId: g.attempt_parent_session_id, childId: g.attempt_child_id,
provider: g.attempt_provider, model: g.attempt_model,
statusLine: g.status_line }))

@@ -279,3 +291,4 @@ : g.status_line

? h("div", { onClick: (e) => e.stopPropagation() },
h(LiveStrip, { parentId: c.parent_session_id, childId: c.child_id }))
h(LiveStrip, { parentId: c.parent_session_id, childId: c.child_id,
provider: c.provider, model: c.model }))
: null)),

@@ -282,0 +295,0 @@ );

+1
-1
// g-174:标题栏显示的插件版本(快速通道:硬编码当前包版本,不做版本号自动同步机制)
const PLUGIN_VERSION = "0.7.1";
const PLUGIN_VERSION = "0.7.2";

@@ -4,0 +4,0 @@ const STAGES = [

@@ -396,4 +396,5 @@ }

// g-128:新增信息收集任务组件(弹窗内信息收集区)——支持标题+kind 选择
// g-198:支持 onRefresh 回调,创建成功后立即通知父组件刷新卡片列表
function AddCardBox(props) {
const { goalId, supervisorSession } = props;
const { goalId, supervisorSession, onRefresh } = props;
const [mode, setMode] = React.useState("idle"); // idle | naming | chat

@@ -421,2 +422,3 @@ const [title, setTitle] = React.useState("");

setMode("idle");
onRefresh?.();
} else {

@@ -423,0 +425,0 @@ setNote("⚠️ 创建失败:" + (data.error || "未知错误"));

@@ -363,6 +363,8 @@ // g-150:handoff 组件(显示当前有效 handoff + 登记新 handoff)

// g-109 判据反馈:最新 attempt 无 child_id(子代理启动失败)时也给出「重新执行」兜底区
const att = (d.attempts ?? []).filter((a) => a.child_id).slice(-1)[0];
const anyAtt = (d.attempts ?? []).length > 0;
// g-107 & g-200:实时会话面板(仅展示 Goal 自身的执行 attempt,排除收集子代理)
const att = (d.attempts ?? []).filter((a) => a.child_id && a.executor !== "agent:collect").slice(-1)[0];
const anyAtt = (d.attempts ?? []).filter((a) => a.executor !== "agent:collect").length > 0;
livePanel = att
? h(SessionPanel, { parentId: att.parent_session_id, childId: att.child_id, collapsible: true,
provider: att.provider, model: att.model, modelRoute: att.model_route,
statusLine: lastAtt?.status_line ?? null,

@@ -436,3 +438,3 @@ goalId: props.id, relaunchKind: "exec",

? h("div", { style: { ...S.meta, marginTop: 4 } }, "(backlog 目标不能创建上下文卡片,请先排期)")
: h(AddCardBox, { goalId: props.id, supervisorSession: props.supervisorSession }))
: h(AddCardBox, { goalId: props.id, supervisorSession: props.supervisorSession, onRefresh: load }))
: h("div", { key: "k", style: S.modalSection },

@@ -443,3 +445,3 @@ h("div", { style: S.modalH }, "🔎 信息收集"),

? h("div", { style: { ...S.meta, marginTop: 4 } }, "(backlog 目标不能创建上下文卡片,请先排期)")
: h(AddCardBox, { goalId: props.id, supervisorSession: props.supervisorSession })),
: h(AddCardBox, { goalId: props.id, supervisorSession: props.supervisorSession, onRefresh: load })),
];

@@ -446,0 +448,0 @@ // g-150:执行上下文 tab(handoff + 最近指令 + 评论)

@@ -1,2 +0,3 @@

const snap = useSessionSnapshot(session);
// g-195: 使用 useThrottledLiveSession 限制 peek 流式刷新为 ≤5fps (≥200ms)
const { snap, line, running } = useThrottledLiveSession(session, 200);
const usage = useProjectionValue(session, "tokenUsage");

@@ -7,5 +8,4 @@ const pressure = useProjectionValue(session, "contextPressure");

return h("div", { style: S.liveStrip, title: props.childId },
"🔌 会话未接入(不在会话列表):" + props.childId.slice(0, 8));
"⚠️ 会话未接入(不在会话列表):" + props.childId.slice(0, 8));
}
const running = !!(snap && snap.running);
// g-a92e1406 追加(负责人指示):新一轮开始(running false→true)时清空上次 status,

@@ -30,4 +30,3 @@ // 等 supervisor 快速替换成最新——记录 running 翻转时刻,旧于它的状态视为过期清空。

const staleDur = staleStatus && props.statusAt != null ? fmtElapsed(props.statusAt, now) : null;
const line = snap && snap.chat ? lastStreamLine(snap.chat.legacy.partial) : null;
const meter = liveMeter(usage, pressure);
const meter = liveMeter(usage, pressure);
// g-129 负责人 2026-08-22 格式:第一行 = 状态 + 流式内容(同行,流式时有时无不引起高度变化),

@@ -49,5 +48,6 @@ // 右侧有足够宽度时显示 tok/ctx;第二行 = status_line 固定显示。

textOverflow: "ellipsis", whiteSpace: "nowrap" } }, "…");
const modelTitle = props.model ? `模型:${props.provider ? props.provider + "/" : ""}${props.model}` : null;
return h(
"div",
{ style: S.liveStrip, title: [statusFull, props.statusLine ? "状态:" + props.statusLine : null, meter ? "资源:" + meter : null, line ? "流式:" + line : null].filter(Boolean).join("\n") },
{ style: S.liveStrip, title: [statusFull, props.statusLine ? "状态:" + props.statusLine : null, modelTitle, meter ? "资源:" + meter : null, line ? "流式:" + line : null].filter(Boolean).join("\n") },
// 第一行:状态 + 流式内容(同行);右侧有空间时显示 tok/ctx(flex 布局自动压缩)

@@ -58,2 +58,5 @@ h("div", { style: { display: "flex", alignItems: "center", gap: 5 } },

lineEl,
props.model
? h("span", { style: { ...S.meta, fontSize: 9, flexShrink: 0, opacity: 0.85, padding: "0 2px" }, title: modelTitle }, props.model)
: null,
meter

@@ -182,2 +185,38 @@ ? h("span", { style: { ...S.meta, fontSize: 10, flexShrink: 0, marginLeft: 4 } }, meter)

// 误导负责人。现改为失败即报错,由调用方用「重新执行指定路由」或「查询不可用」兜底。
// g-194: 格式化模型展示文案,优雅降级,绝不向用户展示 owned by subagent routing 等内部错误
function formatModelDisplay(dynamicModel, staticProvider, staticModel, staticRoute, relaunchRoute, modelErr) {
if (dynamicModel && dynamicModel.model) {
const p = dynamicModel.provider ? `${dynamicModel.provider}/` : "";
return `${p}${dynamicModel.model}` + (dynamicModel.fromParent ? "(父会话,子代理继承)" : "");
}
if (staticProvider || staticModel) {
if (staticProvider && staticModel) return `${staticProvider}/${staticModel}`;
if (staticModel) return `${staticModel}(继承/默认 provider)`;
return `${staticProvider}(继承/默认 model)`;
}
if (staticRoute) {
return staticRoute;
}
if (relaunchRoute) {
return `按重新执行指定:${relaunchRoute}`;
}
if (modelErr) {
// 如果包含内部 routing 错误或私有 session 错误,转为安全的“默认配置/未指定”或“会话已隔离”
if (typeof modelErr === "string" && (modelErr.includes("owned by subagent routing") || modelErr.includes("agent-busy") || modelErr.includes("session"))) {
return "默认配置/未指定";
}
return "不可用:" + modelErr;
}
return "默认配置/未指定";
}
function formatShortModelDisplay(dynamicModel, staticProvider, staticModel, staticRoute, relaunchRoute) {
if (dynamicModel && dynamicModel.model) return dynamicModel.model;
if (staticModel) return staticModel;
if (staticRoute) return String(staticRoute).split("/").pop();
if (relaunchRoute) return "重派:" + String(relaunchRoute).split("/").pop();
return null;
}
// 完整实时面板(抽屉/详情用):实时条 + 模型 + 直达指令 + 最近记录。
function useSessionModel(sessionId, parentId) {

@@ -338,6 +377,9 @@ const [model, setModel] = React.useState(null); // {provider, model, fromParent}

// g-109 判据反馈:sessions.models 对子代理查询失败时,用「重新执行指定路由」兜底(绝不用父会话模型冒充)
// g-194: 优先消费服务端下发的静态 provider / model / model_route,消除 subagent routing 报错
const relaunchRoute = props.relaunchRoute ?? null;
const modelText = model
? `${model.provider}/${model.model}` + (model.fromParent ? "(父会话,子代理继承)" : "")
: relaunchRoute ? `按重新执行指定:${relaunchRoute}` : modelErr ? "不可用:" + modelErr : "查询中…";
const staticProvider = props.provider ?? null;
const staticModel = props.model ?? null;
const staticRoute = props.modelRoute ?? null;
const modelText = formatModelDisplay(model, staticProvider, staticModel, staticRoute, relaunchRoute, modelErr);
const shortModel = formatShortModelDisplay(model, staticProvider, staticModel, staticRoute, relaunchRoute);
// 折叠态标题行的内联摘要:状态 + statusLine + token/ctx + 模型短名

@@ -348,3 +390,3 @@ const collapsedBits = [

meter || null,
model ? model.model : relaunchRoute ? "重派:" + String(relaunchRoute).split("/").pop() : null,
shortModel,
].filter(Boolean).join(" | ");

@@ -370,2 +412,3 @@ return h(

h(LiveStrip, { key: "s", parentId: props.parentId, childId: props.childId,
provider: staticProvider, model: staticModel,
statusLine }),

@@ -372,0 +415,0 @@ h("div", { key: "m", style: { ...S.meta, marginTop: 3 } },

@@ -77,2 +77,66 @@ // 数据源:sessions.binding(childId).session(uSES 快照 subscribe/getSnapshot),

// g-195: 子代理实时流式 peek 节流 hook(≤5fps / ≥200ms 刷新上限)
// 仅用于 peek 展示(LiveStrip / SessionPanel 折叠态等),普通高频推送合并到 trailing flush;
// 流结束/错误/运行态翻转等关键边界在下一合法时间槽完成最终呈现,不丢尾包,卸载时清理定时器。
function useThrottledLiveSession(session, intervalMs = 200) {
const [liveState, setLiveState] = React.useState(() => {
const snap = session ? session.getSnapshot() : null;
return {
snap,
line: snap && snap.chat ? lastStreamLine(snap.chat.legacy.partial) : null,
running: !!(snap && snap.running),
};
});
React.useEffect(() => {
if (!session) {
setLiveState({ snap: null, line: null, running: false });
return;
}
let timer = null;
let lastFlush = 0;
let unmounted = false;
const flush = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
lastFlush = Date.now();
if (unmounted) return;
const s = session.getSnapshot();
setLiveState({
snap: s,
line: s && s.chat ? lastStreamLine(s.chat.legacy.partial) : null,
running: !!(s && s.running),
});
};
const onUpdate = () => {
const now = Date.now();
const elapsed = now - lastFlush;
if (elapsed >= intervalMs) {
flush();
} else if (!timer) {
// Trailing edge: 在剩余时间槽排期执行 flush,保证 ≤5fps (≥200ms) 且不丢尾包
timer = setTimeout(flush, intervalMs - elapsed);
}
};
// 初始同步一次最新状态
flush();
const unsub = session.subscribe(onUpdate);
return () => {
unmounted = true;
if (timer) clearTimeout(timer);
if (typeof unsub === "function") unsub();
};
}, [session, intervalMs]);
return liveState;
}
// 投影值(faceOf 返回 identity-stable 的 uSES face;投影推送不要求 open,看板常驻)

@@ -79,0 +143,0 @@ function useProjectionValue(session, key) {

{
"name": "dsh-graph",
"version": "0.7.1",
"version": "0.7.2",
"description": "dsh-graph 单包(host+client 合并,g-116):把 dsh-graph 核心层包装为 DSH cordis 插件,同时提供 graph_* 目标生命周期工具(建卡/判据/迁移/派发/评审/交付)、/api/dsh-graph REST 端点与浏览器二维泳道看板(conversation.view)。包名 = repo 名(负责人定案);内部 host 插件 id 保留 dsh-graph-host。",

@@ -46,9 +46,5 @@ "license": "MIT",

"dependencies": {
"@deepseek-ai/schemastery": "^3.18.1"
"@deepseek-ai/schemastery": "^3.18.1",
"yaml": "^2.9.0"
},
"scripts": {
"prepack": "bash ../scripts/sync-core.sh && node -e \"require('node:fs').existsSync('core/ops.js') || (console.error('core 未同步'), process.exit(1))\"",
"build": "bash ../scripts/sync-core.sh",
"test": "node --test ../core/tests/*.test.ts"
},
"dsh": {

@@ -65,3 +61,7 @@ "bundle": {

}
},
"scripts": {
"build": "bash ../scripts/sync-core.sh",
"test": "node --test ../core/tests/*.test.ts"
}
}
}

@@ -19,2 +19,4 @@ ---

(更新 `supervisor.session`、记 `supervisor.claimed` 事件、返回 HANDOFF 全文);
- **查阅长期记忆索引**:初始化/接管时,**务必先阅读 `.dsh-graph/memory/long-term/INDEX.md`**,
掌握既有架构模式、历史教训与避坑规范;
- **防争抢**:若 `supervisor.session` 已指向其他会话且负责人没要求你接管,则

@@ -157,3 +159,3 @@ **不要 claim**——保持普通会话身份,等负责人明确指示。

等待输入」等完成态——看板如实反映空闲/完成状态;
- `graph_start_attempt` 派发执行;**status_line 由执行子代理自己更新**
- `graph_start_attempt` 派发执行;传入可选 `card` 参数时统一派发卡片收集(自动生成完整收集提示词并绑定卡片);**status_line 由执行/收集子代理自己更新**
(`graph_report_status`),**supervisor 绝不替子代理汇报**——卡片上那句话是子代理的

@@ -183,10 +185,5 @@ 自述,代劳即伪造进展。要求子代理**及时**更新:每做一个动作就写一句,**简短

- 冻结脚本路径、验收命令逐条写全;
- **worktree 隔离**:并发/复杂的执行任务,子代理先 `git worktree add` 独立工作树
(与 main 隔离)再改代码,review 交付阶段由 supervisor 复核通过后合并回 main——
避免并发子代理互相踩提交、半成品直接落 main。**「直接 main」仅限真正的一两行、
唯一文件改动、且无其他目标并发改该文件;多目标并发改同一文件时必须 worktree**,
不得自认为改动简单就直改 main;
worktree 指令由执行派发默认注入 spawn 提示词,可显式关闭:`graph_start_attempt`
传 `worktree=false`、GUI 端点 `/api/dsh-graph/start-execution` 传 body
`worktree: false`;
- **worktree 隔离(Supervisor 强制默认)**:除非 supervisor 在派发时明确写出 override,所有执行/收集/调研/实现子代理都必须先 `git worktree add` 独立工作树(与 main 隔离)再改代码;**子代理不得自行判断「简单」而绕过隔离,也不得直接修改 main**。supervisor 调用 `graph_start_attempt` 时必须显式传 `worktree=true`(并在 attempt brief 与提示词中写明专属 worktree 路径);只有以下两类例外可直接 main:① supervisor 明确传 `worktree=false` 并在 brief 记录理由的真正一两行、唯一文件小修;② supervisor 自己进行文档/记忆等小修改。没有明确 override 一律视为必须隔离;
review 交付阶段由 supervisor 复核通过后合并到目标集成分支:当前版本合并 main,未来版本合并对应版本测试/集成分支(如 `v0.8-test`),不得把未来版本代码带入 main;避免并发子代理互相踩提交、半成品直接落目标分支;
worktree 指令由执行派发注入 spawn 提示词;GUI 端点仅在 supervisor 明确批准时才可传 body `worktree: false`;
数据分工:代码改动在 worktree,看板数据 `.dsh-graph/` 仍在主工作树写(graph_*

@@ -206,2 +203,18 @@ 工具写的是主工作树的看板/事件流,不被 worktree 分支隔离);

非充分。验证前 **sleep 2s 等文件写入稳定**,避免瞬时误报;
- **并发 Worktree 实施与流水线复核机制**:
- **跨版本/并发特性物理隔离(支持前瞻规划)**:支持在新版本(如 v0.8)规划与并发派发特性;所有并发开发必须在专属 `.worktrees/g-xxx-att-xx` 分支中进行;验证通过后**不合并到 main**,而是**合并到对应版本集成分支(如 `v0.8-test`)**,确保 main 分支的稳定与发版不受未来版本影响,实现真正的全异步并行推进;
- **单目标完工即审**:当派发多个并行 worktree 任务时,某个独立任务一完工,supervisor **立即在该独立 worktree 中启动独立测试实例进行代码与实机复核**,无需阻塞等待所有任务全部完工;
- **并发回报暂存(避免遗忘)**:在复核某一个目标期间,若其他并发子代理发来完成汇报,supervisor 必须**先将回报信息记录/暂存到临时记忆文件(如 `.dsh-graph/memory/review-queue.md`)**;
- **完成取下一个**:当前目标复核完成并标记后,查阅暂存记忆文件按序取出下一个就绪目标继续独立验证,直至队列全部复核完毕;
- **判据自验与勾选规范(区分人类操作与 Supervisor 自验)**:
- **禁止使用 `[x]` 前缀**:页面 Checklist(复选框/进度条)是基于本地 localStorage 供**人类负责人(Human Reviewer)**在 Web UI 上交互打勾与最终把关的;若 Supervisor 在判据前写 `[x]` 会与人类勾选混淆。
- **统一使用 `✅已验` 尾缀**:Supervisor / Review 子代理在完成实机、代码审查与自动化验证后,若判据已严格达标,使用 `graph_set_criteria` 在**每条通过判据文本的末尾追加 `✅已验`**,并在目标评论区追加详尽的测试核验记录与证据;未通过或未测试的项保持原文本不变。
- **复核反馈必须可执行(跨模型对齐)**:发现问题时,不能只报「这里有 bug/请修复」。
每条 blocker/major/minor 都必须同时写清:①**证据**(精确文件/行、触发条件、实际与
预期行为,必要时给最小复现);②**原理/不变量简介**(系统要保护什么、为什么当前
分支违反它,以及影响范围);③**预期修复方式建议**(建议改动的边界/数据流/事务步骤、
必须保留与禁止引入的行为);④**验证方法**(应新增/运行的测试和命令)。用能让另一
个模型直接定位并动手的完整句子,明确哪些是必须修、哪些只是可选方案;不要只依赖
主管与 reviewer 之间的隐含上下文或缩写术语。反馈发给执行子代理时,优先按
「现象 → 原理 → 修复方向 → 验证」顺序,并在返工 prompt 中复述约束。
- 验收脚本(判据中的 `[script]` 项)由规划方在 planning 时冻结,执行方不得修改;

@@ -236,3 +249,3 @@ 脚本报错优先怀疑实现与设计,不是脚本;

信息收集卡| `graph_bind_collect_card` 收集子代理绑卡(parent_session_id 反查会话头)|
`graph_start_attempt` 派发执行(自动绑子代理)| `graph_report_status`
`graph_start_attempt` 派发执行或收集(传 `card` 自动绑卡)| `graph_report_status`
状态汇报| `graph_validate` 全量校验| `graph_rebuild` 事件流对账

@@ -251,3 +264,3 @@

- 目标交付时提炼长期记忆(成功图 / 失败模式 / 偏好),条目必须带来源目标引用;
- **提炼长期记忆与更新索引**:目标交付时提炼长期记忆(成功图 / 失败模式 / 偏好),条目必须带来源目标引用;**新增/修改长期记忆文件时必须同步更新 `.dsh-graph/memory/long-term/INDEX.md` 索引表**;
- 重复出现的任务模式,向负责人提议沉淀为 skill(前瞻式),或把成功的 first run

@@ -254,0 +267,0 @@ 固化为 skill(回溯式)。

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display