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.
Files changed (60) hide show
  1. package/README.md +4 -4
  2. package/dist/apps/panel/index.html +849 -337
  3. package/dist/src/adapters/pi/extension.js +87 -15
  4. package/dist/src/adapters/pi/extension.js.map +1 -1
  5. package/dist/src/adapters/pi/rpc.js +1 -1
  6. package/dist/src/adapters/pi/rpc.js.map +1 -1
  7. package/dist/src/analysis/stats-router.js +1 -1
  8. package/dist/src/analysis/stats-router.js.map +1 -1
  9. package/dist/src/ars/mode-editor.d.ts +1 -5
  10. package/dist/src/ars/mode-editor.js +1 -9
  11. package/dist/src/ars/mode-editor.js.map +1 -1
  12. package/dist/src/branding.d.ts +4 -3
  13. package/dist/src/branding.js +10 -9
  14. package/dist/src/branding.js.map +1 -1
  15. package/dist/src/chat.d.ts +7 -0
  16. package/dist/src/chat.js +19 -1
  17. package/dist/src/chat.js.map +1 -1
  18. package/dist/src/cli-args.d.ts +1 -0
  19. package/dist/src/cli-args.js +1 -0
  20. package/dist/src/cli-args.js.map +1 -1
  21. package/dist/src/cli.js +39 -7
  22. package/dist/src/cli.js.map +1 -1
  23. package/dist/src/index.d.ts +2 -0
  24. package/dist/src/index.js +2 -0
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/literature/knowledge-map.d.ts +4 -0
  27. package/dist/src/literature/knowledge-map.js +4 -0
  28. package/dist/src/literature/knowledge-map.js.map +1 -1
  29. package/dist/src/panel/server.js +9 -109
  30. package/dist/src/panel/server.js.map +1 -1
  31. package/dist/src/project/bootstrap.js +0 -2
  32. package/dist/src/project/bootstrap.js.map +1 -1
  33. package/dist/src/project/workspace.js +3 -3
  34. package/dist/src/project/workspace.js.map +1 -1
  35. package/dist/src/research/choice.d.ts +16 -0
  36. package/dist/src/research/choice.js +25 -0
  37. package/dist/src/research/choice.js.map +1 -0
  38. package/dist/src/session/continuously-work.d.ts +16 -0
  39. package/dist/src/session/continuously-work.js +44 -0
  40. package/dist/src/session/continuously-work.js.map +1 -0
  41. package/dist/src/session/help.d.ts +3 -0
  42. package/dist/src/session/help.js +68 -26
  43. package/dist/src/session/help.js.map +1 -1
  44. package/dist/src/session/modes.js +7 -7
  45. package/dist/src/session/modes.js.map +1 -1
  46. package/dist/src/style/cli-ui.d.ts +1 -0
  47. package/dist/src/style/cli-ui.js +26 -14
  48. package/dist/src/style/cli-ui.js.map +1 -1
  49. package/dist/src/updates/update.d.ts +8 -1
  50. package/dist/src/updates/update.js +68 -2
  51. package/dist/src/updates/update.js.map +1 -1
  52. package/dist/src/verify/checklist.d.ts +46 -5
  53. package/dist/src/verify/checklist.js +139 -24
  54. package/dist/src/verify/checklist.js.map +1 -1
  55. package/dist/src/wake-options/runtime.d.ts +1 -0
  56. package/dist/src/wake-options/runtime.js +12 -2
  57. package/dist/src/wake-options/runtime.js.map +1 -1
  58. package/package.json +1 -1
  59. package/skills/recommended/catalog.json +24 -154
  60. 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 { formatVerifyChecklist, isNaturalPlanConfirm, loadVerifyChecklist, markVerifyItem, skipUnverifiedItems } from "../../verify/checklist.js";
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)), "warning");
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
- "输出可勾选清单,并建议用户打开 /panel 核对页逐项确认;若用户跳过,必须在产物中标注「未经核对」。",
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} 交叉核验;也可 /panel 打开核对清单。`, "info");
1306
+ ctx.ui.notify(`已启动 ${kind} 交叉核验;人审请打开 /panel 核对清单。`, "info");
1300
1307
  return;
1301
1308
  }
1302
1309
  const id = parts[0];
1303
- const status = (parts[1] ?? "verified");
1304
- if (status !== "verified" && status !== "unverified" && status !== "flagged" && status !== "skipped") {
1305
- ctx.ui.notify("Usage: /crosscheck list | skip | kind <citations|format|requirements|stats> | <id> verified|unverified|flagged|skipped [备注]", "info");
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 checklist = await markVerifyItem(ctx.cwd, id, status, notes);
1310
- ctx.ui.notify(formatVerifyChecklist(checklist), "info");
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: "交叉核验:/crosscheck list|skip|kind <类型>|<id> verified|…",
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: "交叉核验(同 /crosscheck):模型提出项,人勾选或 skip 标注未经核对",
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: missing /init or verify does not hard-block results.
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(软门禁):继续执行;关键字段请 AI 核查并由人 /verify。建议先 /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) {