psyclaw 0.30.15 → 0.30.17
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/dist/apps/panel/index.html +52 -17
- package/dist/src/adapters/pi/extension.js +22 -3
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/panel/server.js +93 -17
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/skills/recommended.d.ts +5 -0
- package/dist/src/skills/recommended.js +26 -2
- package/dist/src/skills/recommended.js.map +1 -1
- package/dist/src/tui/skill-manager.js +24 -1
- package/dist/src/tui/skill-manager.js.map +1 -1
- package/dist/src/verify/checklist.d.ts +1 -0
- package/dist/src/verify/checklist.js +18 -2
- package/dist/src/verify/checklist.js.map +1 -1
- package/package.json +1 -1
|
@@ -2573,6 +2573,7 @@ function groupFiles(files) {
|
|
|
2573
2573
|
"核心文档": ordered.filter(path => priority.includes(path)),
|
|
2574
2574
|
"Export 导出结果": ordered.filter(path => path.startsWith("outputs/")),
|
|
2575
2575
|
"论文与报告": ordered.filter(path => /^(paper|docs)\//.test(path) && !priority.includes(path)),
|
|
2576
|
+
"分析报告与核查": ordered.filter(path => path.startsWith("analysis/results/")),
|
|
2576
2577
|
"研究记录": ordered.filter(path => /^(notes|logs)\//.test(path) && !priority.includes(path))
|
|
2577
2578
|
};
|
|
2578
2579
|
}
|
|
@@ -2672,14 +2673,20 @@ async function loadEcosystem() {
|
|
|
2672
2673
|
json("/api/recommended-agents"),
|
|
2673
2674
|
json("/api/enabled-capabilities")
|
|
2674
2675
|
]);
|
|
2675
|
-
const
|
|
2676
|
-
const
|
|
2676
|
+
const skillStatus = new Map((enabled.skills || []).map(item => [item.id, item]));
|
|
2677
|
+
const mcpStatus = new Map((enabled.mcp || []).map(item => [item.id, item]));
|
|
2677
2678
|
state.eco = [
|
|
2678
2679
|
...(enabled.coreSkills || []).map(item => ({ ...item, kind: "core", description: item.description || "PsyClaw 内置科研核心 Skill", sourceRef: "bundled", enabled: item.enabled, installed: true })),
|
|
2679
|
-
...(skills.items || []).map(item =>
|
|
2680
|
+
...(skills.items || []).map(item => {
|
|
2681
|
+
const status = skillStatus.get(item.id) || {};
|
|
2682
|
+
return { ...item, kind: "skill", enabled: Boolean(status.enabled), installed: Boolean(status.installed) };
|
|
2683
|
+
}),
|
|
2680
2684
|
...(plugins.items || []).map(item => ({ ...item, kind: "plugin", enabled: Boolean(item.enabled), installed: Boolean(item.installed) })),
|
|
2681
2685
|
...(enabled.localSkills || []).map(item => ({ ...item, kind: "local", sourceRef: item.path || "用户配置目录", installed: true })),
|
|
2682
|
-
...(mcps.items || []).map(item =>
|
|
2686
|
+
...(mcps.items || []).map(item => {
|
|
2687
|
+
const status = mcpStatus.get(item.id) || {};
|
|
2688
|
+
return { ...item, kind: "mcp", enabled: Boolean(status.enabled), installed: Boolean(status.installed) };
|
|
2689
|
+
}),
|
|
2683
2690
|
...(agents.items || []).map(item => ({ ...item, kind: "agent", enabled: Boolean(item.enabled), installed: Boolean(item.installed), locked: Boolean(item.locked) })),
|
|
2684
2691
|
...(skills.externalTools || []).map(item => ({ ...item, kind: "external", enabled: false, installed: false }))
|
|
2685
2692
|
];
|
|
@@ -2723,7 +2730,13 @@ function renderEcosystem() {
|
|
|
2723
2730
|
$("ecosystem-body").innerHTML = items.map(item => {
|
|
2724
2731
|
const prereqs = itemPrereqs(item);
|
|
2725
2732
|
const busy = Boolean(state.installing[item.id]);
|
|
2726
|
-
const
|
|
2733
|
+
const statusLabel = busy ? "正在安装…"
|
|
2734
|
+
: item.locked ? "内置可用"
|
|
2735
|
+
: item.enabled ? (item.kind === "plugin" ? "已安装" : "已启用")
|
|
2736
|
+
: item.installed ? "已安装未启用"
|
|
2737
|
+
: item.kind === "plugin" && item.configured ? "配置存在,文件缺失"
|
|
2738
|
+
: item.kind === "external" ? "可安装外部工具"
|
|
2739
|
+
: "未安装";
|
|
2727
2740
|
return `
|
|
2728
2741
|
<tr>
|
|
2729
2742
|
<td>
|
|
@@ -2746,25 +2759,46 @@ function renderEcosystem() {
|
|
|
2746
2759
|
</span>
|
|
2747
2760
|
</td>
|
|
2748
2761
|
<td>
|
|
2749
|
-
<span class="tag ${item.enabled ? "ok" : item.configured ? "warn" : ""}">
|
|
2750
|
-
${
|
|
2762
|
+
<span class="tag ${item.enabled ? "ok" : item.installed || item.configured ? "warn" : ""}">
|
|
2763
|
+
${esc(statusLabel)}
|
|
2751
2764
|
</span>
|
|
2752
2765
|
</td>
|
|
2753
2766
|
<td style="text-align:right">
|
|
2754
|
-
${item
|
|
2755
|
-
item.kind === "plugin" && item.local ? "—" :
|
|
2756
|
-
item.kind === "plugin" ? `<button class="button small ${item.installed ? "" : "primary"}" ${busy ? "disabled" : ""} onclick="installCapability('plugin','${esc(item.id)}')">${installLabel}</button>` :
|
|
2757
|
-
item.kind === "external" ? `<button class="button small primary" ${busy ? "disabled" : ""} onclick="installCapability('external','${esc(item.id)}')">${installLabel}</button>` :
|
|
2758
|
-
item.enabled ? `<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>` :
|
|
2759
|
-
item.kind === "local" ? `<button class="button small primary" onclick="toggleCapability('local','${esc(item.id)}',true)">启用</button>` :
|
|
2760
|
-
`<button class="button small primary" ${busy ? "disabled" : ""} onclick="installCapability('${item.kind}','${esc(item.id)}')">${installLabel}</button>`
|
|
2761
|
-
}
|
|
2767
|
+
${capabilityActionButtons(item, busy)}
|
|
2762
2768
|
</td>
|
|
2763
2769
|
</tr>
|
|
2764
2770
|
`;
|
|
2765
2771
|
}).join("") || '<tr><td colspan="5" class="empty" style="min-height:100px">未找到匹配的能力项</td></tr>';
|
|
2766
2772
|
}
|
|
2767
2773
|
|
|
2774
|
+
function capabilityActionButtons(item, busy) {
|
|
2775
|
+
if (item.locked || item.kind === "agent") return "—";
|
|
2776
|
+
if (item.kind === "plugin" && item.local) return "—";
|
|
2777
|
+
if (item.kind === "plugin") {
|
|
2778
|
+
const label = busy ? "正在安装…" : item.installed ? "重新安装" : "安装";
|
|
2779
|
+
return `<button class="button small ${item.installed ? "" : "primary"}" ${busy ? "disabled" : ""} onclick="installCapability('plugin','${esc(item.id)}')">${label}</button>`;
|
|
2780
|
+
}
|
|
2781
|
+
if (item.kind === "external") {
|
|
2782
|
+
return `<button class="button small primary" ${busy ? "disabled" : ""} onclick="installCapability('external','${esc(item.id)}')">${busy ? "正在安装…" : "安装"}</button>`;
|
|
2783
|
+
}
|
|
2784
|
+
if (item.kind === "core" || item.kind === "local") {
|
|
2785
|
+
return item.enabled
|
|
2786
|
+
? `<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>`
|
|
2787
|
+
: `<button class="button small primary" onclick="toggleCapability('${item.kind}','${esc(item.id)}',true)">启用</button>`;
|
|
2788
|
+
}
|
|
2789
|
+
// Recommended Skill / MCP: keep enable/disable separate from install/reinstall.
|
|
2790
|
+
const buttons = [];
|
|
2791
|
+
if (item.installed) {
|
|
2792
|
+
buttons.push(item.enabled
|
|
2793
|
+
? `<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>`
|
|
2794
|
+
: `<button class="button small primary" onclick="toggleCapability('${item.kind}','${esc(item.id)}',true)">启用</button>`);
|
|
2795
|
+
buttons.push(`<button class="button small" ${busy ? "disabled" : ""} onclick="installCapability('${item.kind}','${esc(item.id)}')">${busy ? "正在安装…" : "重新安装"}</button>`);
|
|
2796
|
+
} else {
|
|
2797
|
+
buttons.push(`<button class="button small primary" ${busy ? "disabled" : ""} onclick="installCapability('${item.kind}','${esc(item.id)}')">${busy ? "正在安装…" : "安装"}</button>`);
|
|
2798
|
+
}
|
|
2799
|
+
return buttons.join(" ");
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2768
2802
|
async function toggleCapability(kind, id, enabled) {
|
|
2769
2803
|
await json("/api/recommendation-state", {
|
|
2770
2804
|
method: "POST",
|
|
@@ -2789,14 +2823,15 @@ async function installCapability(kind, id) {
|
|
|
2789
2823
|
const scope = kind === "skill" || kind === "plugin"
|
|
2790
2824
|
? (confirm("安装到系统全局还是当前项目?\n确定:全局所有项目可用;取消:仅当前项目。") ? "user" : "project")
|
|
2791
2825
|
: undefined;
|
|
2792
|
-
|
|
2826
|
+
const reinstall = Boolean(item?.installed);
|
|
2827
|
+
let confirmText = `确认${reinstall ? "重新安装" : "安装"}「${item?.name || id}」?`;
|
|
2793
2828
|
if (kind === "external") {
|
|
2794
2829
|
confirmText += "\n\n系统将按官方可信来源安装;任务会交给当前 PsyClaw 对话中的模型执行。请保持终端会话打开。";
|
|
2795
2830
|
} else if (kind === "mcp" || kind === "skill") {
|
|
2796
2831
|
if (prereqs.length) {
|
|
2797
2832
|
confirmText += `\n\n前置依赖也将一并排队安装:${prereqs.join("、")}。\n安装任务会交给当前 PsyClaw 对话中的模型执行;请保持终端会话打开,完成后执行 /reload。`;
|
|
2798
2833
|
} else {
|
|
2799
|
-
confirmText +=
|
|
2834
|
+
confirmText += `\n\n${reinstall ? "将覆盖当前安装目录并重新交给模型安装" : "安装任务会交给当前 PsyClaw 对话中的模型执行"};请保持终端会话打开,完成后执行 /reload。`;
|
|
2800
2835
|
}
|
|
2801
2836
|
} else {
|
|
2802
2837
|
confirmText += "\n\n安装完成后需要执行 /reload 生效。";
|
|
@@ -36,7 +36,7 @@ import { arsRoot } from "../../ars/pi-panel-executor.js";
|
|
|
36
36
|
import { ArsModeEditor, isArsModeEditorText } from "../../ars/mode-editor.js";
|
|
37
37
|
import { MODE_STATUS, detectChatModeMismatch, formatChatModeMismatchNotice, nextSessionMode, parseSessionMode, sessionModePrompt, } from "../../session/modes.js";
|
|
38
38
|
import { continuouslyWorkWarningText, isContinuouslyWorkEnabled, } from "../../session/continuously-work.js";
|
|
39
|
-
import { assertHumanVerifyGate, formatVerifyChecklist, isNaturalPlanConfirm, isPlanRitualConfirm, PLAN_RITUAL_PHRASE, ensureDefaultVerifyChecklist, loadVerifyChecklist } from "../../verify/checklist.js";
|
|
39
|
+
import { assertHumanVerifyGate, formatVerifyChecklist, isNaturalPlanConfirm, isPlanRitualConfirm, PLAN_RITUAL_PHRASE, ensureDefaultVerifyChecklist, loadVerifyChecklist, recordHumanVerification } from "../../verify/checklist.js";
|
|
40
40
|
import { formatSessionHelp, formatSessionHelpBrief } from "../../session/help.js";
|
|
41
41
|
import { openResearchWorkbench } from "../../panel/workbench.js";
|
|
42
42
|
import { captureAgentError, extractPiGeneration, finishAgentSpan, initNodeObservability, lastPiGeneration, readTelemetryPreference, shutdownObservability, startAgentSpan, trackGateWaiting, trackLlmGeneration, trackSkillInstall, withAgentSpan, writeTelemetryPreference, } from "../../observability/index.js";
|
|
@@ -448,7 +448,7 @@ async function skillManagerRows(root, state) {
|
|
|
448
448
|
scope,
|
|
449
449
|
blocked: typeof item.sourceRef !== "string" || !/^https:\/\//.test(item.sourceRef),
|
|
450
450
|
reason: onDisk
|
|
451
|
-
? `已检测到目录但未通过来源/许可/哈希校验${state.skills.includes(id) ? "(当前配置为启用,但校验失败不会加载)" : ""};可按
|
|
451
|
+
? `已检测到目录但未通过来源/许可/哈希校验${state.skills.includes(id) ? "(当前配置为启用,但校验失败不会加载)" : ""};可按 r 重新安装修复`
|
|
452
452
|
: typeof item.sourceRef === "string" && /^https:\/\//.test(item.sourceRef)
|
|
453
453
|
? "尚未安装;按 Enter 选择安装范围并交给当前模型处理"
|
|
454
454
|
: "没有可交给模型检查的来源网址",
|
|
@@ -599,7 +599,7 @@ async function openMcpManager(ctx, rows) {
|
|
|
599
599
|
return ctx.ui.custom((tui, theme, keybindings, done) => (new SkillManagerComponent(mcpManagerItems(rows), tui, theme, keybindings, done, {
|
|
600
600
|
title: "MCP 管理",
|
|
601
601
|
itemLabel: "MCP",
|
|
602
|
-
footer: "↑/↓ 移动 · Space/Enter 安装或启停 · a 全部开启 · d 全部关闭 · Esc 关闭",
|
|
602
|
+
footer: "↑/↓ 移动 · Space/Enter 安装或启停 · r 重新安装 · a 全部开启 · d 全部关闭 · Esc 关闭",
|
|
603
603
|
enabledText: "已开启;再次点击可关闭",
|
|
604
604
|
disabledText: "已安装,当前未启用;点击可开启",
|
|
605
605
|
missingMessage: "尚未安装;点击交给模型安装并默认启用",
|
|
@@ -1330,6 +1330,25 @@ export default function psyclawExtension(pi) {
|
|
|
1330
1330
|
}
|
|
1331
1331
|
const mode = arsModeEditor?.getMode() ?? sessionMode;
|
|
1332
1332
|
const trimmed = event.text.trim();
|
|
1333
|
+
// A direct user statement is a valid human-review route when Panel is unavailable.
|
|
1334
|
+
// Do not infer approval from assistant output, quoted text, or a question about verification.
|
|
1335
|
+
if (/^我(?:已核验|已核实|已审阅并确认|核验了)(?:分析报告|论文定稿)?[。!!]?$/u.test(trimmed)) {
|
|
1336
|
+
const scope = trimmed.includes("论文定稿") ? "academic-finalize"
|
|
1337
|
+
: trimmed.includes("分析报告") ? "analysis-complete"
|
|
1338
|
+
: mode === "academic" ? "academic-finalize" : mode === "analysis" ? "analysis-complete" : undefined;
|
|
1339
|
+
if (!scope || !(await readActiveProject(ctx.cwd))) {
|
|
1340
|
+
ctx.ui.notify("请先进入已初始化项目的 analysis 或 academic 模式,或明确说「我已核验分析报告」/「我已核验论文定稿」。", "warning");
|
|
1341
|
+
return { action: "handled" };
|
|
1342
|
+
}
|
|
1343
|
+
try {
|
|
1344
|
+
await recordHumanVerification(ctx.cwd, scope, trimmed);
|
|
1345
|
+
ctx.ui.notify(`已记录用户人审确认(${scope});不会自动将分析标为完成或交接。`, "info");
|
|
1346
|
+
}
|
|
1347
|
+
catch (error) {
|
|
1348
|
+
ctx.ui.notify(`人审记录失败:${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1349
|
+
}
|
|
1350
|
+
return { action: "handled" };
|
|
1351
|
+
}
|
|
1333
1352
|
// Chat must not soft-takeover; when intent fits analysis/academic, remind only.
|
|
1334
1353
|
if (mode === "chat") {
|
|
1335
1354
|
const mismatch = detectChatModeMismatch(trimmed);
|