psyclaw 0.28.0 → 0.28.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/README.md +4 -4
- package/agents/recommended/catalog.json +99 -0
- package/dist/apps/panel/index.html +5 -5
- package/dist/src/adapters/pi/extension.js +121 -9
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/adapters/pi/rpc.d.ts +2 -0
- package/dist/src/adapters/pi/rpc.js +9 -2
- package/dist/src/adapters/pi/rpc.js.map +1 -1
- package/dist/src/agents/recommended-personas.d.ts +42 -0
- package/dist/src/agents/recommended-personas.js +135 -0
- package/dist/src/agents/recommended-personas.js.map +1 -0
- package/dist/src/cli.js +6 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/creation/contracts.d.ts +3 -0
- package/dist/src/creation/service.js +17 -5
- package/dist/src/creation/service.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/orchestration/bundled-personas.d.ts +16 -0
- package/dist/src/orchestration/bundled-personas.js +101 -0
- package/dist/src/orchestration/bundled-personas.js.map +1 -0
- package/dist/src/orchestration/effects.d.ts +8 -0
- package/dist/src/orchestration/effects.js +39 -0
- package/dist/src/orchestration/effects.js.map +1 -0
- package/dist/src/orchestration/personas.d.ts +2 -1
- package/dist/src/orchestration/personas.js +20 -7
- package/dist/src/orchestration/personas.js.map +1 -1
- package/dist/src/orchestration/pi-executor.d.ts +9 -5
- package/dist/src/orchestration/pi-executor.js +55 -22
- package/dist/src/orchestration/pi-executor.js.map +1 -1
- package/dist/src/orchestration/research-agents.d.ts +3 -7
- package/dist/src/orchestration/research-agents.js +5 -69
- package/dist/src/orchestration/research-agents.js.map +1 -1
- package/dist/src/panel/server.js +6 -0
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/style/cli-ui.d.ts +14 -0
- package/dist/src/style/cli-ui.js +45 -0
- package/dist/src/style/cli-ui.js.map +1 -1
- package/dist/src/tui/skill-manager.js +1 -1
- package/dist/src/tui/skill-manager.js.map +1 -1
- package/dist/src/updates/update.js +9 -3
- package/dist/src/updates/update.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ PsyClaw 是面向社会科学研究的智能体工作台。它把研究项目、
|
|
|
4
4
|
|
|
5
5
|
PsyClaw 自有代码使用 MIT 许可证;随 npm 包内置的 Academic Research Skills 位于 `vendor/ars`,保持上游署名并单独遵循 CC BY-NC 4.0,仅限非商业用途。
|
|
6
6
|
|
|
7
|
-
当前版本:`0.28.
|
|
7
|
+
当前版本:`0.28.1`。正式命令、用户配置目录和后续发布统一使用 `psyclaw`。
|
|
8
8
|
|
|
9
9
|
## 发布流程
|
|
10
10
|
|
|
@@ -21,19 +21,19 @@ RELEASE_MESSAGE="release: describe the change" pnpm release:push
|
|
|
21
21
|
需要 Node.js `>=22.19.0`。官方 npm 源:
|
|
22
22
|
|
|
23
23
|
```powershell
|
|
24
|
-
npm install -g psyclaw@0.28.
|
|
24
|
+
npm install -g psyclaw@0.28.1
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
如果本机 npm 配置把 registry 误写成带有 `~/` 的地址,请显式指定官方源:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install -g psyclaw@0.28.
|
|
30
|
+
npm install -g psyclaw@0.28.1 --registry=https://registry.npmjs.org/
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
中国大陆网络较慢或无法访问官方源时:
|
|
34
34
|
|
|
35
35
|
```powershell
|
|
36
|
-
npm install -g psyclaw@0.28.
|
|
36
|
+
npm install -g psyclaw@0.28.1 --registry=https://registry.npmmirror.com
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
PsyClaw 的 npm 发布包直接携带 Windows x64/arm64 版 `ripgrep` 和 `fd`,Windows 启动时直接使用包内可执行文件,不再联网下载。其他平台缺少这两个工具时仍由锁定的 Pi 原生工具管理器处理:代理环境走 GitHub 官方 API 与 Release,国内 npm registry 环境统一走国内镜像。
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "psyclaw/recommended-agents/v1",
|
|
3
|
+
"documentVersion": "0.28.1",
|
|
4
|
+
"syncedAt": "2026-09-06",
|
|
5
|
+
"sourceDocument": "bundled literature/review subagents",
|
|
6
|
+
"sourceRef": "bundled",
|
|
7
|
+
"repositoryHint": "对齐 Claude Code:Subagent = md 提示合订本。外仓可后续以 GitHub 源加入 installPrep;当前内置 8 个无需安装。",
|
|
8
|
+
"items": [
|
|
9
|
+
{
|
|
10
|
+
"id": "landscape-mapper",
|
|
11
|
+
"name": "研究版图分析者",
|
|
12
|
+
"kind": "agent",
|
|
13
|
+
"pack": "literature",
|
|
14
|
+
"role": "researcher",
|
|
15
|
+
"stage": "文献多智能体",
|
|
16
|
+
"description": "梳理研究主题、年代分布、代表性论文和研究方向。",
|
|
17
|
+
"sourceRef": "bundled",
|
|
18
|
+
"bundled": true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "methods-scout",
|
|
22
|
+
"name": "方法学分析者",
|
|
23
|
+
"kind": "agent",
|
|
24
|
+
"pack": "literature",
|
|
25
|
+
"role": "analyst",
|
|
26
|
+
"stage": "文献多智能体",
|
|
27
|
+
"description": "识别研究设计、测量与分析线索,区分元数据可判断与不可判断之处。",
|
|
28
|
+
"sourceRef": "bundled",
|
|
29
|
+
"bundled": true
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "evidence-critic",
|
|
33
|
+
"name": "证据批判者",
|
|
34
|
+
"kind": "agent",
|
|
35
|
+
"pack": "literature",
|
|
36
|
+
"role": "critic",
|
|
37
|
+
"stage": "文献多智能体",
|
|
38
|
+
"description": "检查证据强弱、偏倚、冲突与因果边界。",
|
|
39
|
+
"sourceRef": "bundled",
|
|
40
|
+
"bundled": true
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "gap-finder",
|
|
44
|
+
"name": "研究缺口分析者",
|
|
45
|
+
"kind": "agent",
|
|
46
|
+
"pack": "literature",
|
|
47
|
+
"role": "verifier",
|
|
48
|
+
"stage": "文献多智能体",
|
|
49
|
+
"description": "寻找尚未充分覆盖的问题、对象、情境和方法。",
|
|
50
|
+
"sourceRef": "bundled",
|
|
51
|
+
"bundled": true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "theory-reviewer",
|
|
55
|
+
"name": "理论与贡献审稿人",
|
|
56
|
+
"kind": "agent",
|
|
57
|
+
"pack": "review",
|
|
58
|
+
"role": "critic",
|
|
59
|
+
"stage": "同行评审多智能体",
|
|
60
|
+
"description": "检查问题重要性、理论定位、创新性与主张—证据一致性。",
|
|
61
|
+
"sourceRef": "bundled",
|
|
62
|
+
"bundled": true
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "methods-reviewer",
|
|
66
|
+
"name": "研究设计审稿人",
|
|
67
|
+
"kind": "agent",
|
|
68
|
+
"pack": "review",
|
|
69
|
+
"role": "analyst",
|
|
70
|
+
"stage": "同行评审多智能体",
|
|
71
|
+
"description": "检查抽样、设计、测量、效度与因果推断边界。",
|
|
72
|
+
"sourceRef": "bundled",
|
|
73
|
+
"bundled": true
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "statistics-reviewer",
|
|
77
|
+
"name": "统计与可复现性审稿人",
|
|
78
|
+
"kind": "agent",
|
|
79
|
+
"pack": "review",
|
|
80
|
+
"role": "verifier",
|
|
81
|
+
"stage": "同行评审多智能体",
|
|
82
|
+
"description": "检查分析策略、效应量、置信区间与可复现信息(不重算统计)。",
|
|
83
|
+
"sourceRef": "bundled",
|
|
84
|
+
"bundled": true
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "ethics-editorial-reviewer",
|
|
88
|
+
"name": "伦理与编辑审稿人",
|
|
89
|
+
"kind": "agent",
|
|
90
|
+
"pack": "review",
|
|
91
|
+
"role": "writer",
|
|
92
|
+
"stage": "同行评审多智能体",
|
|
93
|
+
"description": "检查伦理、透明报告、引用表达与可读性。",
|
|
94
|
+
"sourceRef": "bundled",
|
|
95
|
+
"bundled": true
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"installPrep": []
|
|
99
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<div class="layout">
|
|
18
18
|
<nav class="nav" aria-label="Panel 导航">
|
|
19
19
|
<button class="active" data-view="project">项目文件</button>
|
|
20
|
-
<button data-view="ecosystem">Skill / Plugin / MCP</button>
|
|
20
|
+
<button data-view="ecosystem">Skill / Plugin / MCP / Agent</button>
|
|
21
21
|
<button data-view="provider">Provider 与成本</button>
|
|
22
22
|
</nav>
|
|
23
23
|
<main class="main">
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
</section>
|
|
34
34
|
|
|
35
35
|
<section id="view-ecosystem" class="view">
|
|
36
|
-
<div class="page-head"><div><h1>Skill / Plugin / MCP 生态</h1><p>在同一页面管理研究 Skill、Pi Plugin 与
|
|
37
|
-
<div class="controls" style="margin-bottom:12px"><input id="eco-search" placeholder="搜索名称或描述"><div class="segmented" aria-label="能力类型"><button class="active" data-eco-kind="all">全部</button><button data-eco-kind="skill">Skill</button><button data-eco-kind="plugin">Plugin</button><button data-eco-kind="mcp">MCP</button></div><select id="eco-kind" aria-label="更多类型"><option value="all">全部来源</option><option value="local">用户 Skill</option><option value="external">外部工具</option></select><select id="eco-status"><option value="all">全部状态</option><option value="enabled">已启用</option><option value="available">未启用</option></select></div>
|
|
36
|
+
<div class="page-head"><div><h1>Skill / Plugin / MCP / Agent 生态</h1><p>在同一页面管理研究 Skill、Pi Plugin、MCP 与 Subagent(对齐 Claude Code)。配置变更后使用 <code>/reload</code>。</p></div><button id="refresh-ecosystem" class="button">刷新</button></div>
|
|
37
|
+
<div class="controls" style="margin-bottom:12px"><input id="eco-search" placeholder="搜索名称或描述"><div class="segmented" aria-label="能力类型"><button class="active" data-eco-kind="all">全部</button><button data-eco-kind="skill">Skill</button><button data-eco-kind="plugin">Plugin</button><button data-eco-kind="mcp">MCP</button><button data-eco-kind="agent">Agent</button></div><select id="eco-kind" aria-label="更多类型"><option value="all">全部来源</option><option value="local">用户 Skill</option><option value="external">外部工具</option></select><select id="eco-status"><option value="all">全部状态</option><option value="enabled">已启用</option><option value="available">未启用</option></select></div>
|
|
38
38
|
<div class="table-wrap"><table class="data"><thead><tr><th>名称</th><th>类型</th><th>来源</th><th>状态</th><th>操作</th></tr></thead><tbody id="ecosystem-body"><tr><td colspan="5">读取中...</td></tr></tbody></table></div>
|
|
39
39
|
</section>
|
|
40
40
|
|
|
@@ -65,8 +65,8 @@ function selectTree(button){document.querySelectorAll(".tree-item").forEach(item
|
|
|
65
65
|
async function openFile(path,button){selectTree(button);$("viewer-path").textContent=path;$("viewer-kind").textContent="读取中";$("viewer-body").className="viewer-body empty";$("viewer-body").textContent="读取中...";try{const file=await json(`/api/project-file?path=${encodeURIComponent(path)}`);$("viewer-kind").textContent=file.format;$("viewer-body").className="viewer-body";$("viewer-body").innerHTML=file.format==="markdown"?`<article class="markdown">${renderMarkdown(file.content)}</article>`:`<pre class="raw">${esc(file.format==="json"?JSON.stringify(JSON.parse(file.content),null,2):file.content)}</pre>`}catch(error){$("viewer-body").className="viewer-body empty";$("viewer-body").textContent=error.message}}
|
|
66
66
|
async function loadProject(){const files=await json("/api/project-files");state.files=files.files||[];renderTree();const goal=document.querySelector('[data-file="notes/goal.md"]')||document.querySelector("[data-file]");if(goal)goal.click()}
|
|
67
67
|
|
|
68
|
-
async function loadEcosystem(){try{const[skills,plugins,mcps,enabled]=await Promise.all([json("/api/recommended-skills"),json("/api/recommended-plugins"),json("/api/recommended-mcps"),json("/api/enabled-capabilities")]);const skillSet=new Set((enabled.skills||[]).map(item=>item.id)),mcpSet=new Set((enabled.mcp||[]).map(item=>item.id));state.eco=[...(enabled.coreSkills||[]).map(item=>({...item,kind:"core",description:item.description||"PsyClaw 内置 Skill",sourceRef:"bundled",enabled:item.enabled,installed:true})),...(skills.items||[]).map(item=>({...item,kind:"skill",enabled:skillSet.has(item.id),installed:skillSet.has(item.id)})),...(plugins.items||[]).map(item=>({...item,kind:"plugin",enabled:Boolean(item.enabled),installed:Boolean(item.installed)})),...(enabled.localSkills||[]).map(item=>({...item,kind:"local",sourceRef:item.path||"用户目录",installed:true})),...(mcps.items||[]).map(item=>({...item,kind:"mcp",enabled:mcpSet.has(item.id),installed:mcpSet.has(item.id)})),...(skills.externalTools||[]).map(item=>({...item,kind:"external",enabled:false,installed:false}))];renderEcosystem()}catch(error){$("ecosystem-body").innerHTML=`<tr><td colspan="5">${esc(error.message)}</td></tr>`}}
|
|
69
|
-
function renderEcosystem(){const query=$("eco-search").value.trim().toLowerCase(),sourceKind=$("eco-kind").value,status=$("eco-status").value,primaryKind=state.ecoKind;const primaryMatches=item=>primaryKind==="all"||(primaryKind==="skill"?(item.kind==="skill"||item.kind==="core"||item.kind==="local"):item.kind===primaryKind);const items=state.eco.filter(item=>primaryMatches(item)&&(sourceKind==="all"||item.kind===sourceKind)&&(status==="all"||(status==="enabled"?item.enabled:!item.enabled))&&`${item.name} ${item.description||""}`.toLowerCase().includes(query));$("ecosystem-body").innerHTML=items.map(item=>`<tr><td><div class="name">${esc(item.name)}</div><div class="subtle">${esc(item.description||"")}${item.kind==="plugin"&&item.scope?` · ${item.scope==="project"?"当前项目":"所有项目"}${item.filtered?" · 已限定资源范围":""}`:""}</div></td><td>${esc(item.kind==="core"?"Core Skill":item.kind==="local"?"用户 Skill":item.kind.toUpperCase())}</td><td>${/^https?:/.test(item.sourceRef||"")?`<a href="${esc(item.sourceRef)}" target="_blank" rel="noreferrer">来源</a>`:esc(item.sourceRef||"bundled")}</td><td><span class="tag ${item.enabled?"ok":item.configured?"warn":""}">${item.locked?"
|
|
68
|
+
async function loadEcosystem(){try{const[skills,plugins,mcps,agents,enabled]=await Promise.all([json("/api/recommended-skills"),json("/api/recommended-plugins"),json("/api/recommended-mcps"),json("/api/recommended-agents"),json("/api/enabled-capabilities")]);const skillSet=new Set((enabled.skills||[]).map(item=>item.id)),mcpSet=new Set((enabled.mcp||[]).map(item=>item.id));state.eco=[...(enabled.coreSkills||[]).map(item=>({...item,kind:"core",description:item.description||"PsyClaw 内置 Skill",sourceRef:"bundled",enabled:item.enabled,installed:true})),...(skills.items||[]).map(item=>({...item,kind:"skill",enabled:skillSet.has(item.id),installed:skillSet.has(item.id)})),...(plugins.items||[]).map(item=>({...item,kind:"plugin",enabled:Boolean(item.enabled),installed:Boolean(item.installed)})),...(enabled.localSkills||[]).map(item=>({...item,kind:"local",sourceRef:item.path||"用户目录",installed:true})),...(mcps.items||[]).map(item=>({...item,kind:"mcp",enabled:mcpSet.has(item.id),installed:mcpSet.has(item.id)})),...(agents.items||[]).map(item=>({...item,kind:"agent",enabled:Boolean(item.enabled),installed:Boolean(item.installed),locked:Boolean(item.locked)})),...(skills.externalTools||[]).map(item=>({...item,kind:"external",enabled:false,installed:false}))];renderEcosystem()}catch(error){$("ecosystem-body").innerHTML=`<tr><td colspan="5">${esc(error.message)}</td></tr>`}}
|
|
69
|
+
function renderEcosystem(){const query=$("eco-search").value.trim().toLowerCase(),sourceKind=$("eco-kind").value,status=$("eco-status").value,primaryKind=state.ecoKind;const primaryMatches=item=>primaryKind==="all"||(primaryKind==="skill"?(item.kind==="skill"||item.kind==="core"||item.kind==="local"):item.kind===primaryKind);const items=state.eco.filter(item=>primaryMatches(item)&&(sourceKind==="all"||item.kind===sourceKind)&&(status==="all"||(status==="enabled"?item.enabled:!item.enabled))&&`${item.name} ${item.description||""}`.toLowerCase().includes(query));$("ecosystem-body").innerHTML=items.map(item=>`<tr><td><div class="name">${esc(item.name)}</div><div class="subtle">${esc(item.description||"")}${item.kind==="plugin"&&item.scope?` · ${item.scope==="project"?"当前项目":"所有项目"}${item.filtered?" · 已限定资源范围":""}`:""}${item.kind==="agent"&&item.slashCommand?` · ${esc(item.slashCommand)}`:""}</div></td><td>${esc(item.kind==="core"?"Core Skill":item.kind==="local"?"用户 Skill":item.kind==="agent"?"Agent":item.kind.toUpperCase())}</td><td>${/^https?:/.test(item.sourceRef||"")?`<a href="${esc(item.sourceRef)}" target="_blank" rel="noreferrer">来源</a>`:esc(item.sourceRef||"bundled")}</td><td><span class="tag ${item.enabled?"ok":item.configured?"warn":""}">${item.locked?"内置可用":item.enabled?(item.kind==="plugin"?"已安装":"已启用"):item.kind==="plugin"&&item.configured?"配置存在,文件缺失":item.kind==="external"?"可安装外部工具":"未启用"}</span></td><td>${item.locked||item.kind==="agent"?"—":item.kind==="plugin"&&item.local?"—":item.kind==="plugin"?`<button class="button small ${item.installed?"":"primary"}" onclick="installCapability('plugin','${esc(item.id)}')">${item.installed?"重新安装":"安装"}</button>`:item.kind==="external"?`<button class="button small primary" onclick="installCapability('external','${esc(item.id)}')">安装</button>`:item.enabled?`<button class="button small" onclick="toggleCapability('${item.kind}','${esc(item.id)}',false)">停用</button>`:item.kind==="local"?`<button class="button small primary" onclick="toggleCapability('local','${esc(item.id)}',true)">启用</button>`:`<button class="button small primary" onclick="installCapability('${item.kind}','${esc(item.id)}')">${item.kind==="core"?"启用":"安装"}</button>`}</td></tr>`).join("")||'<tr><td colspan="5">没有匹配项</td></tr>'}
|
|
70
70
|
async function toggleCapability(kind,id,enabled){await json("/api/recommendation-state",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind:kind==="core"?"core-skill":kind==="skill"?"skills":kind==="local"?"local-skill":kind,id,enabled})});await loadEcosystem()}
|
|
71
71
|
async function installCapability(kind,id){if(kind==="core"){await toggleCapability(kind,id,true);return}const scope=kind==="skill"||kind==="plugin"?(confirm("安装到系统目录?\n确定:所有项目使用;取消:仅当前项目使用。")?"user":"project"):undefined;if(!confirm(`确认安装 ${id}?${kind==="external"?"模型将按官方来源安装并验证。":"安装完成后需要 /reload。"}`))return;const result=await json("/api/install/execute",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({kind,id,scope,approved:true,actor:"researcher"})});alert(result.message||(result.ok?"安装任务已处理。":"安装未完成"));await loadEcosystem()}
|
|
72
72
|
[$("eco-search"),$("eco-kind"),$("eco-status")].forEach(control=>control.addEventListener("input",renderEcosystem));
|
|
@@ -2,7 +2,9 @@ import { DefaultPackageManager, getAgentDir, SettingsManager } from "@earendil-w
|
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { appendApproval, approvalInputDigest, asProject, assertResearchDecision, bootstrapProject, projectPaths, readApprovals, resolveResearchDecision, runOfflineBrief, runInstitutionalFulltext, runLiteratureReview, runExpertReview, runAnalysisDelegation, runWritingReview, runMetaAnalysis, createStageRunner, exportAcademicDocument, recordCitationUse, runParallelPeerReview, writeHandoff } from "../../index.js";
|
|
4
4
|
import { runPlanWithPi } from "../../orchestration/pi-executor.js";
|
|
5
|
-
import { customPersonaPlan,
|
|
5
|
+
import { customPersonaPlan, parseAgentsRequest } from "../../orchestration/personas.js";
|
|
6
|
+
import { elevatedEffects, formatEffects, hasElevatedEffects, normalizeEffects, toolsForEffects } from "../../orchestration/effects.js";
|
|
7
|
+
import { agentManagerRows, loadSelectablePersonas } from "../../agents/recommended-personas.js";
|
|
6
8
|
import { atomicWriteFile } from "../../project/jsonl.js";
|
|
7
9
|
import { applyCreation, previewCreation, runArsMultiAgentBridge } from "../../index.js";
|
|
8
10
|
import { loadUserRules, userRulesPrompt } from "../../rules/user-rules.js";
|
|
@@ -643,6 +645,46 @@ async function openPluginManager(ctx, rows) {
|
|
|
643
645
|
missingMessage: "按 Enter 安装到当前项目。",
|
|
644
646
|
})));
|
|
645
647
|
}
|
|
648
|
+
function agentManagerItems(rows) {
|
|
649
|
+
return rows.map((row) => ({
|
|
650
|
+
id: row.id,
|
|
651
|
+
name: row.name,
|
|
652
|
+
description: row.description,
|
|
653
|
+
status: row.status,
|
|
654
|
+
sourceRef: row.sourceRef,
|
|
655
|
+
details: [
|
|
656
|
+
`类别:${row.stage}`,
|
|
657
|
+
`角色:${row.role}`,
|
|
658
|
+
`来源:${row.source === "bundled" ? "内置 Subagent(类似 Claude Explore/Plan)" : "项目 .psyclaw/agents/custom"}`,
|
|
659
|
+
`运行:/agents --agent ${row.id} <只读研究任务>`,
|
|
660
|
+
],
|
|
661
|
+
}));
|
|
662
|
+
}
|
|
663
|
+
async function openAgentManager(ctx, rows) {
|
|
664
|
+
return ctx.ui.custom((tui, theme, keybindings, done) => (new SkillManagerComponent(agentManagerItems(rows), tui, theme, keybindings, done, {
|
|
665
|
+
title: "Subagent(对齐 Claude Code)",
|
|
666
|
+
itemLabel: "Subagent",
|
|
667
|
+
footer: "↑/↓ 移动 · Enter 查看用法 · Esc 关闭 · 运行: /agents --agent <id> <任务>",
|
|
668
|
+
enterAction: "details",
|
|
669
|
+
toggleEnabled: false,
|
|
670
|
+
enabledText: "已可选用",
|
|
671
|
+
lockedMessage: "内置 Subagent 始终可用。运行:/agents --agent <id> <只读研究任务>",
|
|
672
|
+
})));
|
|
673
|
+
}
|
|
674
|
+
async function showAgentManager(ctx, statusOnly = false) {
|
|
675
|
+
const rows = await agentManagerRows(ctx.cwd);
|
|
676
|
+
if (!ctx.hasUI || typeof ctx.ui.custom !== "function" || statusOnly) {
|
|
677
|
+
ctx.ui.notify(rows.map((row) => `${row.status === "core" ? "◆" : "●"} ${row.id} — ${row.name}(${row.stage})`).join("\n")
|
|
678
|
+
|| "暂无 Subagent。可用 /create-subagent 创建(类似 Claude Code)。", "info");
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
while (true) {
|
|
682
|
+
const action = await openAgentManager(ctx, await agentManagerRows(ctx.cwd));
|
|
683
|
+
if (action.type === "close")
|
|
684
|
+
return;
|
|
685
|
+
// Enter/Space only show inline notices; keep the manager open.
|
|
686
|
+
}
|
|
687
|
+
}
|
|
646
688
|
async function installRecommendedPlugin(ctx, row) {
|
|
647
689
|
if (!row.installable)
|
|
648
690
|
throw new Error(row.reason ?? `${row.name} 尚未提供可安装的 Plugin package`);
|
|
@@ -1848,7 +1890,7 @@ export default function psyclawExtension(pi) {
|
|
|
1848
1890
|
pi.registerTool({
|
|
1849
1891
|
name: "psyclaw_create",
|
|
1850
1892
|
label: "Create project capability",
|
|
1851
|
-
description: "Preview and, after an explicit UI confirmation, create one PsyClaw-native project Skill, declarative analysis Hook, additive Rule, or
|
|
1893
|
+
description: "Preview and, after an explicit UI confirmation, create one PsyClaw-native project Skill, declarative analysis Hook, additive Rule, or Subagent (Claude-style). Elevated subagent effects require confirmation at create and again at each /agents run.",
|
|
1852
1894
|
parameters: Type.Object({
|
|
1853
1895
|
kind: Type.Union([Type.Literal("skill"), Type.Literal("hook"), Type.Literal("rule"), Type.Literal("subagent")]),
|
|
1854
1896
|
id: Type.String({ minLength: 1, maxLength: 64 }),
|
|
@@ -1859,6 +1901,12 @@ export default function psyclawExtension(pi) {
|
|
|
1859
1901
|
pattern: Type.Optional(Type.String({ maxLength: 500 })),
|
|
1860
1902
|
pathPrefix: Type.Optional(Type.String({ maxLength: 200 })),
|
|
1861
1903
|
role: Type.Optional(Type.Union([Type.Literal("planner"), Type.Literal("researcher"), Type.Literal("analyst"), Type.Literal("critic"), Type.Literal("writer"), Type.Literal("verifier")])),
|
|
1904
|
+
allowedEffects: Type.Optional(Type.Array(Type.Union([
|
|
1905
|
+
Type.Literal("read"),
|
|
1906
|
+
Type.Literal("write"),
|
|
1907
|
+
Type.Literal("network"),
|
|
1908
|
+
Type.Literal("destructive"),
|
|
1909
|
+
]), { maxItems: 4 })),
|
|
1862
1910
|
}),
|
|
1863
1911
|
executionMode: "sequential",
|
|
1864
1912
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -1870,7 +1918,15 @@ export default function psyclawExtension(pi) {
|
|
|
1870
1918
|
if (!ctx.hasUI || typeof ctx.ui?.confirm !== "function") {
|
|
1871
1919
|
return { content: [{ type: "text", text: JSON.stringify({ ...preview, status: "preview-only", reason: "interactive confirmation unavailable" }, null, 2) }], details: preview };
|
|
1872
1920
|
}
|
|
1873
|
-
const
|
|
1921
|
+
const effects = request.kind === "subagent" ? normalizeEffects(request.allowedEffects) : ["read"];
|
|
1922
|
+
const elevated = request.kind === "subagent" && hasElevatedEffects(effects);
|
|
1923
|
+
const title = elevated
|
|
1924
|
+
? `创建 Subagent “${preview.id}”(含提升权限)?`
|
|
1925
|
+
: `创建 ${params.kind} “${preview.id}”?`;
|
|
1926
|
+
const body = elevated
|
|
1927
|
+
? `目标:${preview.path}\n声明 effects:${formatEffects(effects)}\n将开放工具:${toolsForEffects(effects).join(", ")}\nSHA-256:${preview.sha256}\n\n每次 /agents 运行仍会再次确认。凭据读取与绕过门禁仍被禁止。\n\n${preview.contents}`
|
|
1928
|
+
: `目标:${preview.path}\nSHA-256:${preview.sha256}\n\n${preview.contents}`;
|
|
1929
|
+
const approved = await ctx.ui.confirm(title, body);
|
|
1874
1930
|
if (!approved)
|
|
1875
1931
|
return { content: [{ type: "text", text: "Creation cancelled; no file was written." }], details: { status: "cancelled", preview } };
|
|
1876
1932
|
const result = await applyCreation(ctx.cwd, request, preview.sha256, true);
|
|
@@ -2121,12 +2177,22 @@ export default function psyclawExtension(pi) {
|
|
|
2121
2177
|
// runner behind PSYCLAW_DEVELOPER_COMMANDS in published builds.
|
|
2122
2178
|
if (!legacyTestApi)
|
|
2123
2179
|
pi.registerCommand("agents", {
|
|
2124
|
-
description: "
|
|
2180
|
+
description: "浏览或运行 Subagent(对齐 Claude Code)",
|
|
2125
2181
|
handler: async (args, ctx) => {
|
|
2182
|
+
const trimmed = args.trim();
|
|
2183
|
+
if (!trimmed || trimmed === "status" || trimmed === "list") {
|
|
2184
|
+
try {
|
|
2185
|
+
await showAgentManager(ctx, trimmed === "status" || trimmed === "list");
|
|
2186
|
+
}
|
|
2187
|
+
catch (error) {
|
|
2188
|
+
await notifyError(ctx, error);
|
|
2189
|
+
}
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2126
2192
|
const requested = parseAgentsRequest(args);
|
|
2127
2193
|
const objective = requested.objective;
|
|
2128
2194
|
if (!objective) {
|
|
2129
|
-
ctx.ui.notify("Usage: /agents [--agent <id>|--agents <id,...>] <
|
|
2195
|
+
ctx.ui.notify("Usage: /agents [--agent <id>|--agents <id,...>] <task>\n无任务时直接运行 /agents 打开 Subagent 管理页。", "info");
|
|
2130
2196
|
return;
|
|
2131
2197
|
}
|
|
2132
2198
|
if (objective.length > 4_000) {
|
|
@@ -2151,13 +2217,60 @@ export default function psyclawExtension(pi) {
|
|
|
2151
2217
|
const runId = `pi_agent_${Date.now()}`;
|
|
2152
2218
|
let plan = researchTaskPlan(runId, objective);
|
|
2153
2219
|
if (requested.ids.length > 0) {
|
|
2154
|
-
const available = await
|
|
2220
|
+
const available = await loadSelectablePersonas(ctx.cwd);
|
|
2155
2221
|
const byId = new Map(available.map((persona) => [persona.id, persona]));
|
|
2156
2222
|
const selected = requested.ids.map((id) => byId.get(id));
|
|
2157
2223
|
const missing = requested.ids.filter((_id, index) => !selected[index]);
|
|
2158
2224
|
if (missing.length > 0)
|
|
2159
|
-
throw new Error(`Unknown or invalid
|
|
2160
|
-
|
|
2225
|
+
throw new Error(`Unknown or invalid subagent: ${missing.join(", ")}. Run /agents to list bundled and custom subagents.`);
|
|
2226
|
+
const personas = selected.filter((persona) => persona !== undefined);
|
|
2227
|
+
const unionEffects = normalizeEffects(personas.flatMap((persona) => persona.allowedEffects));
|
|
2228
|
+
const elevated = elevatedEffects(unionEffects);
|
|
2229
|
+
if (elevated.length > 0) {
|
|
2230
|
+
if (!ctx.hasUI || typeof ctx.ui.confirm !== "function") {
|
|
2231
|
+
throw new Error(`Subagent effects [${formatEffects(elevated)}] require interactive confirmation`);
|
|
2232
|
+
}
|
|
2233
|
+
const approved = await ctx.ui.confirm("批准 Subagent 提升权限?", `将启用:${formatEffects(elevated)}\n对应工具:${toolsForEffects(unionEffects).join(", ")}\n任务:${objective.slice(0, 400)}\n\n凭据读取、绕过门禁与外部发布仍被禁止。`);
|
|
2234
|
+
if (!approved) {
|
|
2235
|
+
ctx.ui.notify("已取消:未批准提升权限,未启动 Subagent。", "warning");
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
plan = customPersonaPlan(runId, objective, personas);
|
|
2240
|
+
await mkdir(join(ctx.cwd, ".psyclaw", "plans"), { recursive: true });
|
|
2241
|
+
await atomicWriteFile(join(ctx.cwd, ".psyclaw", "plans", `${runId}.json`), `${JSON.stringify(plan, null, 2)}\n`);
|
|
2242
|
+
const eventLog = new RunEventLog(ctx.cwd, runId);
|
|
2243
|
+
const result = await runPlanWithPi(plan, {
|
|
2244
|
+
cwd: ctx.cwd,
|
|
2245
|
+
agentDir: join(ctx.cwd, ".psyclaw", "pi-agent"),
|
|
2246
|
+
...(ctx.model?.provider === undefined ? {} : { provider: ctx.model.provider }),
|
|
2247
|
+
...(ctx.model?.id === undefined ? {} : { model: ctx.model.id }),
|
|
2248
|
+
env: providerEnvironment(ctx.model?.provider),
|
|
2249
|
+
...(elevated.length === 0 ? { tools: [] } : {}),
|
|
2250
|
+
allowWrites: unionEffects.includes("write"),
|
|
2251
|
+
allowNetwork: unionEffects.includes("network"),
|
|
2252
|
+
allowDestructive: unionEffects.includes("destructive"),
|
|
2253
|
+
root: ctx.cwd,
|
|
2254
|
+
pauseRequested: async () => {
|
|
2255
|
+
try {
|
|
2256
|
+
await import("node:fs/promises").then(({ access }) => access(join(ctx.cwd, ".psyclaw", "runs", `${runId}.pause`)));
|
|
2257
|
+
return true;
|
|
2258
|
+
}
|
|
2259
|
+
catch {
|
|
2260
|
+
return false;
|
|
2261
|
+
}
|
|
2262
|
+
},
|
|
2263
|
+
onEvent: async (event) => { await eventLog.append(event); },
|
|
2264
|
+
});
|
|
2265
|
+
pi.appendEntry("psyclaw:agent-run", {
|
|
2266
|
+
runId,
|
|
2267
|
+
status: result.status,
|
|
2268
|
+
diagnostics: result.diagnostics,
|
|
2269
|
+
effects: unionEffects,
|
|
2270
|
+
recordedAt: new Date().toISOString(),
|
|
2271
|
+
});
|
|
2272
|
+
ctx.ui.notify(`Agent run ${result.status}: ${result.diagnostics.join("; ") || "verified"}`, result.status === "completed" ? "info" : "warning");
|
|
2273
|
+
return;
|
|
2161
2274
|
}
|
|
2162
2275
|
await mkdir(join(ctx.cwd, ".psyclaw", "plans"), { recursive: true });
|
|
2163
2276
|
await atomicWriteFile(join(ctx.cwd, ".psyclaw", "plans", `${runId}.json`), `${JSON.stringify(plan, null, 2)}\n`);
|
|
@@ -2168,7 +2281,6 @@ export default function psyclawExtension(pi) {
|
|
|
2168
2281
|
...(ctx.model?.provider === undefined ? {} : { provider: ctx.model.provider }),
|
|
2169
2282
|
...(ctx.model?.id === undefined ? {} : { model: ctx.model.id }),
|
|
2170
2283
|
env: providerEnvironment(ctx.model?.provider),
|
|
2171
|
-
...(requested.ids.length > 0 ? { tools: [] } : {}),
|
|
2172
2284
|
root: ctx.cwd,
|
|
2173
2285
|
pauseRequested: async () => {
|
|
2174
2286
|
try {
|