psyclaw 0.29.17 → 0.29.19

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 (49) hide show
  1. package/README.md +7 -4
  2. package/dist/apps/panel/index.html +441 -154
  3. package/dist/apps/panel/observability.js +87 -0
  4. package/dist/src/adapters/pi/extension.js +40 -2
  5. package/dist/src/adapters/pi/extension.js.map +1 -1
  6. package/dist/src/chat.js +2 -0
  7. package/dist/src/chat.js.map +1 -1
  8. package/dist/src/cli.js +63 -0
  9. package/dist/src/cli.js.map +1 -1
  10. package/dist/src/observability/config.d.ts +47 -0
  11. package/dist/src/observability/config.js +125 -0
  12. package/dist/src/observability/config.js.map +1 -0
  13. package/dist/src/observability/index.d.ts +29 -0
  14. package/dist/src/observability/index.js +75 -0
  15. package/dist/src/observability/index.js.map +1 -0
  16. package/dist/src/observability/node-sdks.d.ts +11 -0
  17. package/dist/src/observability/node-sdks.js +92 -0
  18. package/dist/src/observability/node-sdks.js.map +1 -0
  19. package/dist/src/observability/notice.d.ts +9 -0
  20. package/dist/src/observability/notice.js +43 -0
  21. package/dist/src/observability/notice.js.map +1 -0
  22. package/dist/src/observability/preference.d.ts +23 -0
  23. package/dist/src/observability/preference.js +102 -0
  24. package/dist/src/observability/preference.js.map +1 -0
  25. package/dist/src/observability/public-keys.d.ts +10 -0
  26. package/dist/src/observability/public-keys.js +11 -0
  27. package/dist/src/observability/public-keys.js.map +1 -0
  28. package/dist/src/orchestration/runner.js +21 -2
  29. package/dist/src/orchestration/runner.js.map +1 -1
  30. package/dist/src/panel/extension.js +5 -1
  31. package/dist/src/panel/extension.js.map +1 -1
  32. package/dist/src/panel/hub.d.ts +2 -0
  33. package/dist/src/panel/hub.js +12 -0
  34. package/dist/src/panel/hub.js.map +1 -1
  35. package/dist/src/panel/server.d.ts +4 -0
  36. package/dist/src/panel/server.js +188 -75
  37. package/dist/src/panel/server.js.map +1 -1
  38. package/dist/src/panel/workbench.js +15 -1
  39. package/dist/src/panel/workbench.js.map +1 -1
  40. package/dist/src/session/modes.d.ts +13 -0
  41. package/dist/src/session/modes.js +39 -3
  42. package/dist/src/session/modes.js.map +1 -1
  43. package/dist/src/style/cli-ui.js +2 -0
  44. package/dist/src/style/cli-ui.js.map +1 -1
  45. package/docs/PsyClaw/344/275/277/347/224/250/347/231/275/347/232/256/344/271/246_v0.29.15.md +907 -0
  46. package/docs//344/275/277/347/224/250/347/231/275/347/232/256/344/271/246.md +3 -0
  47. package/package.json +7 -2
  48. package/scripts/rebrand-pi.mjs +74 -1
  49. package/skills/recommended/catalog.json +4 -2
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Opt-in browser observability for PsyClaw panel + website.
3
+ *
4
+ * Loads Sentry / PostHog from a CDN only when a DSN or project key is present
5
+ * on window.__PSYCLAW_OBS__, a meta tag, or (local debug) a query param.
6
+ * With those empty, this file is a no-op: no network calls, no SDK init.
7
+ */
8
+ (function psyclawObservability() {
9
+ if (window.__PSYCLAW_OBS_BOOTED__) return;
10
+ window.__PSYCLAW_OBS_BOOTED__ = true;
11
+
12
+ function meta(name) {
13
+ const node = document.querySelector('meta[name="' + name + '"]');
14
+ const value = node && node.getAttribute("content");
15
+ return value ? String(value).trim() : "";
16
+ }
17
+
18
+ function param(name) {
19
+ try {
20
+ return new URLSearchParams(location.search).get(name) || "";
21
+ } catch {
22
+ return "";
23
+ }
24
+ }
25
+
26
+ function readConfig() {
27
+ const fromWindow = window.__PSYCLAW_OBS__ || {};
28
+ const sentryDsn = String(fromWindow.sentryDsn || meta("psyclaw-sentry-dsn") || param("sentryDsn") || "").trim();
29
+ const posthogKey = String(fromWindow.posthogKey || meta("psyclaw-posthog-key") || param("posthogKey") || "").trim();
30
+ const posthogHost = String(
31
+ fromWindow.posthogHost || meta("psyclaw-posthog-host") || param("posthogHost") || "https://us.posthog.com",
32
+ ).trim();
33
+ const surface = fromWindow.surface === "panel" || fromWindow.surface === "website" ? fromWindow.surface : "web";
34
+ const release = String(fromWindow.release || "").trim();
35
+ return { sentryDsn, posthogKey, posthogHost, surface, release };
36
+ }
37
+
38
+ async function loadEsm(url) {
39
+ return import(url);
40
+ }
41
+
42
+ async function bootSentry(config) {
43
+ const Sentry = await loadEsm("https://cdn.jsdelivr.net/npm/@sentry/browser@10/+esm");
44
+ Sentry.init({
45
+ dsn: config.sentryDsn,
46
+ release: config.release || undefined,
47
+ environment: "local",
48
+ tracesSampleRate: 1.0,
49
+ sendDefaultPii: false,
50
+ integrations: typeof Sentry.browserTracingIntegration === "function" ? [Sentry.browserTracingIntegration()] : [],
51
+ });
52
+ window.Sentry = Sentry;
53
+ }
54
+
55
+ async function bootPostHog(config) {
56
+ const mod = await loadEsm("https://cdn.jsdelivr.net/npm/posthog-js@1/+esm");
57
+ const posthog = mod.default || mod.posthog || mod;
58
+ const panel = config.surface === "panel";
59
+ posthog.init(config.posthogKey, {
60
+ api_host: config.posthogHost,
61
+ person_profiles: "identified_only",
62
+ autocapture: true,
63
+ capture_pageview: true,
64
+ capture_pageleave: true,
65
+ persistence: "memory",
66
+ mask_all_text: panel,
67
+ mask_all_element_attributes: panel,
68
+ session_recording: {
69
+ maskAllInputs: true,
70
+ maskTextSelector: panel
71
+ ? ".markdown, .raw, .viewer-body, .event-detail, pre, code, textarea, [data-file]"
72
+ : undefined,
73
+ },
74
+ loaded: function (client) {
75
+ client.register({ surface: config.surface, app: "psyclaw" });
76
+ },
77
+ });
78
+ window.posthog = posthog;
79
+ }
80
+
81
+ const config = readConfig();
82
+ const jobs = [];
83
+ if (config.sentryDsn) jobs.push(bootSentry(config).catch(function () {}));
84
+ if (config.posthogKey) jobs.push(bootPostHog(config).catch(function () {}));
85
+ if (jobs.length === 0) return;
86
+ Promise.all(jobs).catch(function () {});
87
+ })();
@@ -30,11 +30,12 @@ import { advanceAnalysisPlan, createAnalysisPlan, formatAnalysisPlanStatus, read
30
30
  import { formatAnalysisPlanTakeover, resolveStatsIntent, } from "../../analysis/stats-router.js";
31
31
  import { arsRoot } from "../../ars/pi-panel-executor.js";
32
32
  import { ArsModeEditor, isArsModeEditorText } from "../../ars/mode-editor.js";
33
- import { MODE_STATUS, nextSessionMode, parseSessionMode, sessionModePrompt, } from "../../session/modes.js";
33
+ import { MODE_STATUS, detectChatModeMismatch, formatChatModeMismatchNotice, nextSessionMode, parseSessionMode, sessionModePrompt, } from "../../session/modes.js";
34
34
  import { continuouslyWorkWarningText, isContinuouslyWorkEnabled, } from "../../session/continuously-work.js";
35
35
  import { assertHumanVerifyGate, formatVerifyChecklist, isNaturalPlanConfirm, ensureDefaultVerifyChecklist, loadVerifyChecklist } from "../../verify/checklist.js";
36
36
  import { formatSessionHelp, formatSessionHelpBrief } from "../../session/help.js";
37
37
  import { openResearchWorkbench } from "../../panel/workbench.js";
38
+ import { captureAgentError, initNodeObservability, readTelemetryPreference, shutdownObservability, trackGateWaiting, writeTelemetryPreference, } from "../../observability/index.js";
38
39
  /** Codex-style slash surface: bare command, or command + trailing free text. No subcommand trees. */
39
40
  function parseInitArgs(args) {
40
41
  const goal = args.trim();
@@ -43,6 +44,7 @@ function parseInitArgs(args) {
43
44
  return { paradigm: "survey-observational", goal };
44
45
  }
45
46
  async function notifyError(ctx, error) {
47
+ await captureAgentError(error, { phase: "extension" });
46
48
  ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
47
49
  }
48
50
  const activeAgentRuns = new Set();
@@ -887,6 +889,7 @@ const WORKFLOW_RUNNERS = {
887
889
  "expert-review": runExpertReview,
888
890
  };
889
891
  export default function psyclawExtension(pi) {
892
+ void initNodeObservability();
890
893
  const developerCommands = process.env.PSYCLAW_DEVELOPER_COMMANDS === "1";
891
894
  const legacyTestApi = typeof pi.registerTool !== "function";
892
895
  const runtimeMcps = new RuntimeMcpRegistry();
@@ -936,7 +939,10 @@ export default function psyclawExtension(pi) {
936
939
  };
937
940
  });
938
941
  if (!legacyTestApi && typeof pi.on === "function")
939
- pi.on("session_shutdown", () => runtimeMcps.close());
942
+ pi.on("session_shutdown", () => {
943
+ runtimeMcps.close();
944
+ void shutdownObservability();
945
+ });
940
946
  if (!legacyTestApi && typeof pi.on === "function")
941
947
  pi.on("before_agent_start", async (event, ctx) => {
942
948
  if (!(await readActiveProject(ctx?.cwd ?? process.cwd())))
@@ -1017,6 +1023,17 @@ export default function psyclawExtension(pi) {
1017
1023
  }
1018
1024
  const mode = arsModeEditor?.getMode() ?? sessionMode;
1019
1025
  const trimmed = event.text.trim();
1026
+ // Chat must not soft-takeover; when intent fits analysis/academic, remind only.
1027
+ if (mode === "chat") {
1028
+ const mismatch = detectChatModeMismatch(trimmed);
1029
+ if (mismatch) {
1030
+ ctx.ui.notify(mismatch.notify, "warning");
1031
+ return {
1032
+ action: "transform",
1033
+ text: formatChatModeMismatchNotice(mismatch, trimmed),
1034
+ };
1035
+ }
1036
+ }
1020
1037
  // Natural-language plan confirm: reply「可以」instead of forcing /plan confirm.
1021
1038
  if (mode === "analysis" && isNaturalPlanConfirm(trimmed)) {
1022
1039
  const plan = await readActiveAnalysisPlan(ctx.cwd);
@@ -1094,6 +1111,7 @@ export default function psyclawExtension(pi) {
1094
1111
  if (requiresSeparateOperationConfirmation(event.toolName, event.input)) {
1095
1112
  if (!ctx.hasUI)
1096
1113
  return { block: true, terminate: true, reason: "外部发布需要用户在交互界面中明确确认" };
1114
+ void trackGateWaiting("tool_approval");
1097
1115
  const choice = await ctx.ui.select(`确认外部操作\n${summary}`, ["确认执行", "取消"], { timeout: 120_000 });
1098
1116
  const approved = choice === "确认执行";
1099
1117
  await appendApproval(ctx.cwd, {
@@ -1607,6 +1625,26 @@ export default function psyclawExtension(pi) {
1607
1625
  ctx.ui.notify(`启动横幅宠物已${action === "on" ? "开启" : "关闭"},下次启动生效`, "info");
1608
1626
  },
1609
1627
  });
1628
+ if (!legacyTestApi)
1629
+ pi.registerCommand("telemetry", {
1630
+ description: "查看或关闭匿名产品遥测",
1631
+ handler: async (args, ctx) => {
1632
+ const action = args.trim().toLowerCase() || "status";
1633
+ if (action === "status") {
1634
+ const preference = await readTelemetryPreference();
1635
+ ctx.ui.notify(preference.enabled
1636
+ ? "匿名产品遥测:开启(默认)。采集粗粒度使用与错误,不含研究正文。关闭:/telemetry off"
1637
+ : "匿名产品遥测:已关闭。重新开启:/telemetry on", "info");
1638
+ return;
1639
+ }
1640
+ if (action !== "on" && action !== "off") {
1641
+ ctx.ui.notify("Usage: /telemetry on|off|status", "error");
1642
+ return;
1643
+ }
1644
+ await writeTelemetryPreference({ enabled: action === "on", noticeAcknowledged: true });
1645
+ ctx.ui.notify(action === "on" ? "已开启匿名产品遥测。下次启动生效。" : "已关闭匿名产品遥测。下次启动不再发送。", "info");
1646
+ },
1647
+ });
1610
1648
  if (typeof pi.registerTool === "function") {
1611
1649
  pi.registerTool({
1612
1650
  name: "psyclaw_research_decision",