psyclaw 0.29.11 → 0.29.13
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/dist/apps/panel/index.html +849 -337
- package/dist/src/adapters/pi/extension.js +87 -15
- package/dist/src/adapters/pi/extension.js.map +1 -1
- package/dist/src/adapters/pi/rpc.js +1 -1
- package/dist/src/adapters/pi/rpc.js.map +1 -1
- package/dist/src/analysis/stats-router.js +1 -1
- package/dist/src/analysis/stats-router.js.map +1 -1
- package/dist/src/ars/mode-editor.d.ts +1 -5
- package/dist/src/ars/mode-editor.js +1 -9
- package/dist/src/ars/mode-editor.js.map +1 -1
- package/dist/src/branding.d.ts +4 -3
- package/dist/src/branding.js +10 -9
- package/dist/src/branding.js.map +1 -1
- package/dist/src/chat.d.ts +7 -0
- package/dist/src/chat.js +19 -1
- package/dist/src/chat.js.map +1 -1
- package/dist/src/cli-args.d.ts +1 -0
- package/dist/src/cli-args.js +1 -0
- package/dist/src/cli-args.js.map +1 -1
- package/dist/src/cli.js +39 -7
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/literature/knowledge-map.d.ts +4 -0
- package/dist/src/literature/knowledge-map.js +4 -0
- package/dist/src/literature/knowledge-map.js.map +1 -1
- package/dist/src/panel/server.js +9 -109
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/project/bootstrap.js +0 -2
- package/dist/src/project/bootstrap.js.map +1 -1
- package/dist/src/project/workspace.js +3 -3
- package/dist/src/project/workspace.js.map +1 -1
- package/dist/src/research/choice.d.ts +16 -0
- package/dist/src/research/choice.js +25 -0
- package/dist/src/research/choice.js.map +1 -0
- package/dist/src/session/continuously-work.d.ts +16 -0
- package/dist/src/session/continuously-work.js +44 -0
- package/dist/src/session/continuously-work.js.map +1 -0
- package/dist/src/session/help.d.ts +3 -0
- package/dist/src/session/help.js +68 -26
- package/dist/src/session/help.js.map +1 -1
- package/dist/src/session/modes.js +7 -7
- package/dist/src/session/modes.js.map +1 -1
- package/dist/src/style/cli-ui.d.ts +1 -0
- package/dist/src/style/cli-ui.js +26 -14
- package/dist/src/style/cli-ui.js.map +1 -1
- package/dist/src/updates/update.d.ts +8 -1
- package/dist/src/updates/update.js +68 -2
- package/dist/src/updates/update.js.map +1 -1
- package/dist/src/verify/checklist.d.ts +46 -5
- package/dist/src/verify/checklist.js +139 -24
- package/dist/src/verify/checklist.js.map +1 -1
- package/dist/src/wake-options/runtime.d.ts +1 -0
- package/dist/src/wake-options/runtime.js +12 -2
- package/dist/src/wake-options/runtime.js.map +1 -1
- package/package.json +1 -1
- package/skills/recommended/catalog.json +24 -154
- package/skills/recommended/mcp-catalog.json +16 -4
|
@@ -18,8 +18,9 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
18
18
|
import { coreSkillNames, enabledRecommendedSkillPaths, normalizeRecommendedSkillId, readRecommendationState, readRecommendedCatalog, recommendedSkillTarget, saveRecommendationState, validateModelInstalledRecommendedSkill, } from "../../skills/recommended.js";
|
|
19
19
|
import { SkillManagerComponent } from "../../tui/skill-manager.js";
|
|
20
20
|
import { WakeOptionsComponent } from "../../tui/wake-options.js";
|
|
21
|
-
import { applyWakeVerifySync, buildWakePrompt, formatWakeResult, labelsFor, waitForPanelWakeAnswer, } from "../../wake-options/runtime.js";
|
|
21
|
+
import { applyWakeVerifySync, buildWakePrompt, createWakeVerifyChecklist, formatWakeResult, labelsFor, waitForPanelWakeAnswer, } from "../../wake-options/runtime.js";
|
|
22
22
|
import { panelHub } from "../../panel/hub.js";
|
|
23
|
+
import { appendChoiceRecord } from "../../research/choice.js";
|
|
23
24
|
import { enabledLocalSkillPaths, enabledLocalPromptPaths, readUserSkillState, scanLocalSkills, setLocalSkillEnabled, setLocalSkillsEnabled, skillNamesInPaths, installLocalSkill, userSkillId, } from "../../skills/user-skills.js";
|
|
24
25
|
import { RuntimeMcpRegistry, setUserMcpConfigEnabled, } from "../../integrations/mcp-runtime.js";
|
|
25
26
|
import { SecretInputComponent, ProviderPickerComponent, } from "../../tui/provider-picker.js";
|
|
@@ -31,7 +32,8 @@ import { formatAnalysisPlanTakeover, resolveStatsIntent, } from "../../analysis/
|
|
|
31
32
|
import { arsRoot } from "../../ars/pi-panel-executor.js";
|
|
32
33
|
import { ArsModeEditor, isArsModeEditorText } from "../../ars/mode-editor.js";
|
|
33
34
|
import { MODE_STATUS, nextSessionMode, parseSessionMode, sessionModePrompt, } from "../../session/modes.js";
|
|
34
|
-
import {
|
|
35
|
+
import { continuouslyWorkWarningText, isContinuouslyWorkEnabled, } from "../../session/continuously-work.js";
|
|
36
|
+
import { assertHumanVerifyGate, formatVerifyChecklist, isNaturalPlanConfirm, loadVerifyChecklist, markVerifyItem, skipUnverifiedItems } from "../../verify/checklist.js";
|
|
35
37
|
import { formatSessionHelp, formatSessionHelpBrief } from "../../session/help.js";
|
|
36
38
|
import { openResearchWorkbench } from "../../panel/workbench.js";
|
|
37
39
|
const PARADIGMS = new Set([
|
|
@@ -1109,6 +1111,10 @@ export default function psyclawExtension(pi) {
|
|
|
1109
1111
|
if (!legacyTestApi && typeof pi.on === "function")
|
|
1110
1112
|
pi.on("session_start", (_event, ctx) => {
|
|
1111
1113
|
arsUiContext = ctx;
|
|
1114
|
+
if (isContinuouslyWorkEnabled() && ctx.hasUI) {
|
|
1115
|
+
ctx.ui.setStatus("cw", "continuously-work");
|
|
1116
|
+
ctx.ui.notify(continuouslyWorkWarningText(), "error");
|
|
1117
|
+
}
|
|
1112
1118
|
if (!ctx.hasUI || typeof ctx.ui.setEditorComponent !== "function")
|
|
1113
1119
|
return;
|
|
1114
1120
|
ctx.ui.setEditorComponent((tui, theme, keybindings) => {
|
|
@@ -1285,40 +1291,48 @@ export default function psyclawExtension(pi) {
|
|
|
1285
1291
|
return;
|
|
1286
1292
|
}
|
|
1287
1293
|
if (parts[0] === "skip") {
|
|
1288
|
-
ctx.ui.notify(formatVerifyChecklist(await skipUnverifiedItems(ctx.cwd))
|
|
1294
|
+
ctx.ui.notify(`${formatVerifyChecklist(await skipUnverifiedItems(ctx.cwd))}\n注意:skip 不解除 analysis/academic 人审硬门禁。`, "warning");
|
|
1289
1295
|
return;
|
|
1290
1296
|
}
|
|
1291
1297
|
if (parts[0] === "kind") {
|
|
1292
1298
|
const kind = (parts[1] ?? "general");
|
|
1293
1299
|
pi.sendUserMessage([
|
|
1294
|
-
"
|
|
1300
|
+
"交叉核验任务:请作为助手主动提出核对项并执行字段核查,不要等用户自己去「申请确认」。",
|
|
1295
1301
|
`核验类别:${kind}(citations=引文,format=格式,requirements=研究要求,stats=统计结果,general=综合)。`,
|
|
1296
|
-
"
|
|
1302
|
+
"核查完成后用 /crosscheck <id> verified 记为 AI 已核(ai-checked),不得自称已过人审。",
|
|
1303
|
+
"analysis / academic 模式下,全文或分析收尾前必须请人在 Panel「核实」或 /crosscheck <id> human;跳过不算通过。",
|
|
1297
1304
|
"分析前与分析后都要有清单,不能默认跳过。",
|
|
1298
1305
|
].join("\n"), ctx.isIdle() ? {} : { deliverAs: "followUp" });
|
|
1299
|
-
ctx.ui.notify(`已启动 ${kind}
|
|
1306
|
+
ctx.ui.notify(`已启动 ${kind} 交叉核验;人审请打开 /panel → 核对清单。`, "info");
|
|
1300
1307
|
return;
|
|
1301
1308
|
}
|
|
1302
1309
|
const id = parts[0];
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1310
|
+
const statusRaw = (parts[1] ?? "verified").toLowerCase();
|
|
1311
|
+
const allowed = new Set(["verified", "ai-checked", "unverified", "flagged", "skipped", "human"]);
|
|
1312
|
+
if (!allowed.has(statusRaw)) {
|
|
1313
|
+
ctx.ui.notify("Usage: /crosscheck list | skip | kind <citations|format|requirements|stats> | <id> verified|ai-checked|human|unverified|flagged|skipped [备注]", "info");
|
|
1306
1314
|
return;
|
|
1307
1315
|
}
|
|
1308
1316
|
const notes = parts.slice(2).join(" ") || undefined;
|
|
1309
|
-
const
|
|
1310
|
-
ctx.
|
|
1317
|
+
const source = statusRaw === "human" ? "human" : "ai";
|
|
1318
|
+
const checklist = await markVerifyItem(ctx.cwd, id, statusRaw, notes, undefined, { source });
|
|
1319
|
+
const tip = statusRaw === "human" || (statusRaw === "verified" && source === "human")
|
|
1320
|
+
? "已记为人审通过。"
|
|
1321
|
+
: statusRaw === "verified" || statusRaw === "ai-checked"
|
|
1322
|
+
? "已记为 AI 已核;analysis/academic 收尾前仍须人在 Panel 点「核实」或 /crosscheck <id> human。"
|
|
1323
|
+
: undefined;
|
|
1324
|
+
ctx.ui.notify(tip ? `${formatVerifyChecklist(checklist)}\n${tip}` : formatVerifyChecklist(checklist), "info");
|
|
1311
1325
|
}
|
|
1312
1326
|
catch (error) {
|
|
1313
1327
|
await notifyError(ctx, error);
|
|
1314
1328
|
}
|
|
1315
1329
|
};
|
|
1316
1330
|
pi.registerCommand("crosscheck", {
|
|
1317
|
-
description: "
|
|
1331
|
+
description: "AI 交叉核验 + 人审:/crosscheck list|skip|kind|<id> verified|human|…",
|
|
1318
1332
|
handler: async (args, ctx) => handleCrosscheck(args, ctx),
|
|
1319
1333
|
});
|
|
1320
1334
|
pi.registerCommand("verify", {
|
|
1321
|
-
description: "
|
|
1335
|
+
description: "同 /crosscheck;AI 核查后 analysis/academic 须人审",
|
|
1322
1336
|
handler: async (args, ctx) => handleCrosscheck(args, ctx),
|
|
1323
1337
|
});
|
|
1324
1338
|
pi.registerCommand("help", {
|
|
@@ -1430,6 +1444,11 @@ export default function psyclawExtension(pi) {
|
|
|
1430
1444
|
return;
|
|
1431
1445
|
}
|
|
1432
1446
|
if (cmd === "handoff") {
|
|
1447
|
+
const gate = await assertHumanVerifyGate(ctx.cwd, "analysis-complete");
|
|
1448
|
+
if (!gate.ok) {
|
|
1449
|
+
ctx.ui.notify(gate.message, "error");
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1433
1452
|
const path = await syncHandoffFromAnalysisPlan(ctx.cwd, plan);
|
|
1434
1453
|
if (plan.status === "running") {
|
|
1435
1454
|
plan = advanceAnalysisPlan(plan, plan.scriptEntrypoint
|
|
@@ -1488,6 +1507,19 @@ export default function psyclawExtension(pi) {
|
|
|
1488
1507
|
ctx.ui.notify("已启动学术追问。完成后会先展示文档更新建议,并询问是否写回。", "info");
|
|
1489
1508
|
},
|
|
1490
1509
|
});
|
|
1510
|
+
if (!legacyTestApi)
|
|
1511
|
+
pi.registerCommand("brainstorm", {
|
|
1512
|
+
description: "显式启动研究方向头脑风暴(逐题澄清)",
|
|
1513
|
+
handler: async (args, ctx) => {
|
|
1514
|
+
const subject = args.trim();
|
|
1515
|
+
pi.sendUserMessage([
|
|
1516
|
+
"使用 academic-grill Skill 的头脑风暴入口,先提出 2-4 个有理论意义、可回答的研究方向,比较价值、可行性与边界并推荐一个。",
|
|
1517
|
+
"随后按研究者需要逐题澄清,每轮只问一个实质问题,同时给出推荐答案和主要取舍;不要直接替用户执行完整研究或写论文。",
|
|
1518
|
+
subject ? `本次头脑风暴主题:${subject}` : "请根据当前对话和项目材料确定头脑风暴主题;已有信息不要重复询问。",
|
|
1519
|
+
].join("\n"), ctx.isIdle() ? {} : { deliverAs: "followUp" });
|
|
1520
|
+
ctx.ui.notify("已启动研究方向头脑风暴。完成后可用 /grill 继续严格压力测试。", "info");
|
|
1521
|
+
},
|
|
1522
|
+
});
|
|
1491
1523
|
if (!legacyTestApi)
|
|
1492
1524
|
pi.registerCommand("review", {
|
|
1493
1525
|
description: "并发运行多角色模拟同行评审",
|
|
@@ -2209,11 +2241,11 @@ export default function psyclawExtension(pi) {
|
|
|
2209
2241
|
executionMode: "sequential",
|
|
2210
2242
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
2211
2243
|
try {
|
|
2212
|
-
// Soft gate
|
|
2244
|
+
// Soft gate for missing /init only. Analysis/academic completion still requires human verify.
|
|
2213
2245
|
const project = await readActiveProject(ctx.cwd);
|
|
2214
2246
|
const softNote = project
|
|
2215
2247
|
? undefined
|
|
2216
|
-
: "尚未 /init
|
|
2248
|
+
: "尚未 /init(软门禁):继续执行;收尾前须 AI /crosscheck 后人审。建议先 /init 建仓。";
|
|
2217
2249
|
const request = params.request.trim();
|
|
2218
2250
|
const combined = `${request} ${params.identifier ?? ""}`;
|
|
2219
2251
|
const explicitWorkflow = params.workflow?.trim();
|
|
@@ -2246,6 +2278,13 @@ export default function psyclawExtension(pi) {
|
|
|
2246
2278
|
}
|
|
2247
2279
|
if (publishIntent) {
|
|
2248
2280
|
try {
|
|
2281
|
+
const gate = await assertHumanVerifyGate(ctx.cwd, "academic-finalize");
|
|
2282
|
+
if (!gate.ok) {
|
|
2283
|
+
return {
|
|
2284
|
+
content: [{ type: "text", text: gate.message }],
|
|
2285
|
+
details: { workflow: "publish-manuscript", blocked: true, reason: "human-verify-gate" },
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2249
2288
|
const result = await exportAcademicDocument(ctx.cwd);
|
|
2250
2289
|
return {
|
|
2251
2290
|
content: [{ type: "text", text: JSON.stringify({
|
|
@@ -2324,6 +2363,36 @@ export default function psyclawExtension(pi) {
|
|
|
2324
2363
|
}
|
|
2325
2364
|
},
|
|
2326
2365
|
});
|
|
2366
|
+
pi.registerTool({
|
|
2367
|
+
name: "psyclaw_record_choice",
|
|
2368
|
+
label: "记录选择",
|
|
2369
|
+
description: "记录研究者对选项的回应。结构化唤醒选项或直接文本回应都必须记录;直接回应需保留原文并填写解析后的选项。",
|
|
2370
|
+
parameters: Type.Object({
|
|
2371
|
+
prompt: Type.String({ minLength: 1, description: "当时向研究者提出的问题或选择提示" }),
|
|
2372
|
+
originalResponse: Type.String({ minLength: 1, description: "研究者的原始回应,不能改写或省略" }),
|
|
2373
|
+
selectedIds: Type.Array(Type.String(), { description: "解析后的选项 ID;无法明确解析时为空" }),
|
|
2374
|
+
selectedLabels: Type.Array(Type.String(), { description: "解析后的选项名称" }),
|
|
2375
|
+
source: Type.Union([Type.Literal("tool"), Type.Literal("free-text")]),
|
|
2376
|
+
context: Type.Optional(Type.String({ description: "选择发生时的研究或任务上下文" })),
|
|
2377
|
+
}),
|
|
2378
|
+
executionMode: "sequential",
|
|
2379
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
2380
|
+
try {
|
|
2381
|
+
const record = await appendChoiceRecord(ctx.cwd, {
|
|
2382
|
+
prompt: params.prompt,
|
|
2383
|
+
originalResponse: params.originalResponse,
|
|
2384
|
+
selectedIds: params.selectedIds,
|
|
2385
|
+
selectedLabels: params.selectedLabels,
|
|
2386
|
+
source: params.source,
|
|
2387
|
+
...(params.context === undefined ? {} : { context: params.context }),
|
|
2388
|
+
});
|
|
2389
|
+
return { content: [{ type: "text", text: JSON.stringify(record, null, 2) }], details: record };
|
|
2390
|
+
}
|
|
2391
|
+
catch (error) {
|
|
2392
|
+
return { content: [{ type: "text", text: `选择记录失败:${error instanceof Error ? error.message : String(error)}` }], details: { status: "failed" }, isError: true };
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
});
|
|
2327
2396
|
pi.registerTool({
|
|
2328
2397
|
name: "psyclaw_wake_options",
|
|
2329
2398
|
label: "唤醒选项",
|
|
@@ -2363,6 +2432,9 @@ export default function psyclawExtension(pi) {
|
|
|
2363
2432
|
...(params.syncVerify === undefined ? {} : { syncVerify: params.syncVerify }),
|
|
2364
2433
|
...(params.timeoutMs === undefined ? {} : { timeoutMs: params.timeoutMs }),
|
|
2365
2434
|
});
|
|
2435
|
+
if (wakePrompt.mode === "checklist") {
|
|
2436
|
+
await createWakeVerifyChecklist(ctx.cwd, wakePrompt);
|
|
2437
|
+
}
|
|
2366
2438
|
const timeoutMs = Math.max(5_000, new Date(wakePrompt.expiresAt).getTime() - Date.now());
|
|
2367
2439
|
const panelClients = panelHub.subscriberCount();
|
|
2368
2440
|
if (ctx.hasUI) {
|