denchclaw 2.4.1 → 2.4.3

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 (122) hide show
  1. package/apps/web/.next/standalone/apps/web/.next/BUILD_ID +1 -1
  2. package/apps/web/.next/standalone/apps/web/.next/app-build-manifest.json +82 -82
  3. package/apps/web/.next/standalone/apps/web/.next/app-path-routes-manifest.json +25 -25
  4. package/apps/web/.next/standalone/apps/web/.next/build-manifest.json +2 -2
  5. package/apps/web/.next/standalone/apps/web/.next/cache/config.json +3 -3
  6. package/apps/web/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
  7. package/apps/web/.next/standalone/apps/web/.next/required-server-files.json +1 -1
  8. package/apps/web/.next/standalone/apps/web/.next/routes-manifest.json +1 -1
  9. package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  10. package/apps/web/.next/standalone/apps/web/.next/server/app/api/apps/cron/route_client-reference-manifest.js +1 -1
  11. package/apps/web/.next/standalone/apps/web/.next/server/app/api/apps/proxy/route_client-reference-manifest.js +1 -1
  12. package/apps/web/.next/standalone/apps/web/.next/server/app/api/apps/route_client-reference-manifest.js +1 -1
  13. package/apps/web/.next/standalone/apps/web/.next/server/app/api/apps/serve/[...path]/route_client-reference-manifest.js +1 -1
  14. package/apps/web/.next/standalone/apps/web/.next/server/app/api/apps/store/route_client-reference-manifest.js +1 -1
  15. package/apps/web/.next/standalone/apps/web/.next/server/app/api/apps/webhooks/[...path]/route_client-reference-manifest.js +1 -1
  16. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/active/route_client-reference-manifest.js +1 -1
  17. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/route.js +1 -1
  18. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/route_client-reference-manifest.js +1 -1
  19. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/runs/route_client-reference-manifest.js +1 -1
  20. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stop/route.js +1 -1
  21. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stop/route_client-reference-manifest.js +1 -1
  22. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stream/route_client-reference-manifest.js +1 -1
  23. package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/subagents/route_client-reference-manifest.js +1 -1
  24. package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/jobs/[jobId]/runs/route_client-reference-manifest.js +1 -1
  25. package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/jobs/route_client-reference-manifest.js +1 -1
  26. package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/runs/[sessionId]/route_client-reference-manifest.js +1 -1
  27. package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/runs/search-transcript/route_client-reference-manifest.js +1 -1
  28. package/apps/web/.next/standalone/apps/web/.next/server/app/api/feedback/route.js +1 -1
  29. package/apps/web/.next/standalone/apps/web/.next/server/app/api/feedback/route_client-reference-manifest.js +1 -1
  30. package/apps/web/.next/standalone/apps/web/.next/server/app/api/gateway/channels/route_client-reference-manifest.js +1 -1
  31. package/apps/web/.next/standalone/apps/web/.next/server/app/api/gateway/chat/route_client-reference-manifest.js +1 -1
  32. package/apps/web/.next/standalone/apps/web/.next/server/app/api/gateway/chat/stream/route_client-reference-manifest.js +1 -1
  33. package/apps/web/.next/standalone/apps/web/.next/server/app/api/gateway/sessions/[id]/route_client-reference-manifest.js +1 -1
  34. package/apps/web/.next/standalone/apps/web/.next/server/app/api/gateway/sessions/route_client-reference-manifest.js +1 -1
  35. package/apps/web/.next/standalone/apps/web/.next/server/app/api/memories/route_client-reference-manifest.js +1 -1
  36. package/apps/web/.next/standalone/apps/web/.next/server/app/api/profiles/route_client-reference-manifest.js +1 -1
  37. package/apps/web/.next/standalone/apps/web/.next/server/app/api/profiles/switch/route_client-reference-manifest.js +1 -1
  38. package/apps/web/.next/standalone/apps/web/.next/server/app/api/sessions/[sessionId]/route_client-reference-manifest.js +1 -1
  39. package/apps/web/.next/standalone/apps/web/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  40. package/apps/web/.next/standalone/apps/web/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  41. package/apps/web/.next/standalone/apps/web/.next/server/app/api/terminal/port/route_client-reference-manifest.js +1 -1
  42. package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/[id]/messages/route_client-reference-manifest.js +1 -1
  43. package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/[id]/route_client-reference-manifest.js +1 -1
  44. package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/route.js +1 -1
  45. package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/route_client-reference-manifest.js +1 -1
  46. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/assets/[...path]/route_client-reference-manifest.js +1 -1
  47. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/browse/route_client-reference-manifest.js +1 -1
  48. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/browse-file/route_client-reference-manifest.js +1 -1
  49. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/context/route_client-reference-manifest.js +1 -1
  50. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/copy/route_client-reference-manifest.js +1 -1
  51. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/db/introspect/route_client-reference-manifest.js +1 -1
  52. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/db/query/route_client-reference-manifest.js +1 -1
  53. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/delete/route.js +1 -1
  54. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/delete/route_client-reference-manifest.js +1 -1
  55. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/execute/route_client-reference-manifest.js +1 -1
  56. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/file/route_client-reference-manifest.js +1 -1
  57. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/init/route.js +1 -1
  58. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/init/route_client-reference-manifest.js +1 -1
  59. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/link-preview/route_client-reference-manifest.js +1 -1
  60. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/list/route_client-reference-manifest.js +1 -1
  61. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/mkdir/route_client-reference-manifest.js +1 -1
  62. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/move/route_client-reference-manifest.js +1 -1
  63. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/actions/route_client-reference-manifest.js +1 -1
  64. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/actions/runs/route_client-reference-manifest.js +1 -1
  65. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/display-field/route_client-reference-manifest.js +1 -1
  66. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/[id]/content/route_client-reference-manifest.js +1 -1
  67. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/[id]/route_client-reference-manifest.js +1 -1
  68. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/bulk-delete/route_client-reference-manifest.js +1 -1
  69. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/options/route_client-reference-manifest.js +1 -1
  70. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/route.js +1 -1
  71. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/route_client-reference-manifest.js +1 -1
  72. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/[fieldId]/enum-rename/route_client-reference-manifest.js +1 -1
  73. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/[fieldId]/route_client-reference-manifest.js +1 -1
  74. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/reorder/route_client-reference-manifest.js +1 -1
  75. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/route_client-reference-manifest.js +1 -1
  76. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/route_client-reference-manifest.js +1 -1
  77. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/views/route_client-reference-manifest.js +1 -1
  78. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/open-file/route_client-reference-manifest.js +1 -1
  79. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/path-info/route_client-reference-manifest.js +1 -1
  80. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/query/route_client-reference-manifest.js +1 -1
  81. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/raw-file/route_client-reference-manifest.js +1 -1
  82. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/rename/route_client-reference-manifest.js +1 -1
  83. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/reports/execute/route.js +1 -1
  84. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/reports/execute/route_client-reference-manifest.js +1 -1
  85. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/search-index/route_client-reference-manifest.js +1 -1
  86. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/suggest-files/route_client-reference-manifest.js +1 -1
  87. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/switch/route.js +1 -1
  88. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/switch/route_client-reference-manifest.js +1 -1
  89. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/thumbnail/route_client-reference-manifest.js +1 -1
  90. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/tree/route_client-reference-manifest.js +1 -1
  91. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/upload/route.js +1 -1
  92. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/upload/route_client-reference-manifest.js +1 -1
  93. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/virtual-file/route_client-reference-manifest.js +1 -1
  94. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/watch/route_client-reference-manifest.js +1 -1
  95. package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/write-binary/route_client-reference-manifest.js +1 -1
  96. package/apps/web/.next/standalone/apps/web/.next/server/app/page.js +1 -1
  97. package/apps/web/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  98. package/apps/web/.next/standalone/apps/web/.next/server/app/workspace/page_client-reference-manifest.js +1 -1
  99. package/apps/web/.next/standalone/apps/web/.next/server/app-paths-manifest.json +25 -25
  100. package/apps/web/.next/standalone/apps/web/.next/server/chunks/6787.js +1 -1
  101. package/apps/web/.next/standalone/apps/web/.next/server/functions-config-manifest.json +9 -9
  102. package/apps/web/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  103. package/apps/web/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  104. package/apps/web/.next/standalone/apps/web/.next/static/chunks/app/{layout-27fe4dc768717757.js → layout-5b42eb12bc6891b1.js} +1 -1
  105. package/apps/web/.next/{static/chunks/app/page-6a172dc187a2f128.js → standalone/apps/web/.next/static/chunks/app/page-4dfaa52589c03e51.js} +1 -1
  106. package/apps/web/.next/standalone/apps/web/server.js +1 -1
  107. package/apps/web/.next/standalone/package.json +2 -1
  108. package/apps/web/.next/static/chunks/app/{layout-27fe4dc768717757.js → layout-5b42eb12bc6891b1.js} +1 -1
  109. package/apps/web/.next/{standalone/apps/web/.next/static/chunks/app/page-6a172dc187a2f128.js → static/chunks/app/page-4dfaa52589c03e51.js} +1 -1
  110. package/dist/entry.js +1 -1
  111. package/dist/{program-DcrLV9Z2.js → program-D2RfLoKh.js} +16 -37
  112. package/dist/{run-main-D7Ux65aN.js → run-main-giTi_or5.js} +1 -1
  113. package/extensions/dench-identity/index.test.ts +136 -0
  114. package/extensions/dench-identity/index.ts +3 -2
  115. package/extensions/posthog-analytics/lib/build-env.js +1 -1
  116. package/extensions/vitest.config.ts +13 -0
  117. package/package.json +2 -1
  118. package/skills/crm/SKILL.md +46 -1
  119. /package/apps/web/.next/standalone/apps/web/.next/static/{nF9C2TCY9WzJYkr3LhlED → yotM7sjmc2mLN7mGVWai0}/_buildManifest.js +0 -0
  120. /package/apps/web/.next/standalone/apps/web/.next/static/{nF9C2TCY9WzJYkr3LhlED → yotM7sjmc2mLN7mGVWai0}/_ssgManifest.js +0 -0
  121. /package/apps/web/.next/static/{nF9C2TCY9WzJYkr3LhlED → yotM7sjmc2mLN7mGVWai0}/_buildManifest.js +0 -0
  122. /package/apps/web/.next/static/{nF9C2TCY9WzJYkr3LhlED → yotM7sjmc2mLN7mGVWai0}/_ssgManifest.js +0 -0
package/dist/entry.js CHANGED
@@ -1147,7 +1147,7 @@ if (!ensureExperimentalWarningSuppressed()) {
1147
1147
  const appliedProfile = applyCliProfileEnv({ profile: parsed.profile ?? void 0 });
1148
1148
  if (appliedProfile.warning) console.warn(`[denchclaw] ${appliedProfile.warning}`);
1149
1149
  process$1.argv = parsed.argv;
1150
- import("./run-main-D7Ux65aN.js").then(({ runCli }) => runCli(process$1.argv)).then(() => import("./telemetry-DguRjCZq.js").then((n) => n.n).then(({ shutdownTelemetry }) => shutdownTelemetry())).catch((error) => {
1150
+ import("./run-main-giTi_or5.js").then(({ runCli }) => runCli(process$1.argv)).then(() => import("./telemetry-DguRjCZq.js").then((n) => n.n).then(({ shutdownTelemetry }) => shutdownTelemetry())).catch((error) => {
1151
1151
  console.error("[denchclaw] Failed to start CLI:", error instanceof Error ? error.stack ?? error.message : error);
1152
1152
  process$1.exitCode = 1;
1153
1153
  });
@@ -2406,21 +2406,17 @@ async function attemptGatewayAutoFix(params) {
2406
2406
  params.profile,
2407
2407
  "gateway",
2408
2408
  "install",
2409
- "--force",
2410
- "--port",
2411
- String(params.gatewayPort)
2409
+ "--force"
2412
2410
  ],
2413
2411
  timeoutMs: 2 * 6e4
2414
2412
  },
2415
2413
  {
2416
- name: "openclaw gateway start",
2414
+ name: "openclaw gateway restart",
2417
2415
  args: [
2418
2416
  "--profile",
2419
2417
  params.profile,
2420
2418
  "gateway",
2421
- "start",
2422
- "--port",
2423
- String(params.gatewayPort)
2419
+ "restart"
2424
2420
  ],
2425
2421
  timeoutMs: 2 * 6e4
2426
2422
  }
@@ -2630,7 +2626,8 @@ function resolveDenchCloudApiKeyCandidate(params) {
2630
2626
  }
2631
2627
  async function promptForDenchCloudApiKey(initialValue) {
2632
2628
  const value = await text({
2633
- message: stylePromptMessage("Enter your Dench Cloud API key (sign up at dench.com and get it at dench.com/settings)"),
2629
+ message: stylePromptMessage("Paste your Dench Cloud API key"),
2630
+ placeholder: "dench_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
2634
2631
  ...initialValue ? { initialValue } : {},
2635
2632
  validate: (input) => input?.trim().length ? void 0 : "API key is required."
2636
2633
  });
@@ -2732,10 +2729,11 @@ async function resolveDenchCloudBootstrapSelection(params) {
2732
2729
  };
2733
2730
  }
2734
2731
  const wantsDenchCloud = explicitRequest ? true : await confirm({
2735
- message: stylePromptMessage("Use Dench API Key for inference? Sign up on dench.com and get your API key at dench.com/settings."),
2732
+ message: stylePromptMessage("Use Dench Cloud for inference? Get your API key at dench.com/api"),
2736
2733
  initialValue: existingDenchConfigured || !currentProvider
2737
2734
  });
2738
2735
  if (isCancel(wantsDenchCloud) || !wantsDenchCloud) return { enabled: false };
2736
+ if (!params.nonInteractive) await openUrl$1("https://dench.com/api").catch(() => {});
2739
2737
  let apiKey = resolveDenchCloudApiKeyCandidate({
2740
2738
  opts: params.opts,
2741
2739
  existingApiKey: existing.apiKey
@@ -3529,22 +3527,7 @@ async function restartGatewayDaemon(params) {
3529
3527
  };
3530
3528
  }
3531
3529
  const s = !params.json ? spinner() : null;
3532
- s?.start("Stopping gateway daemon…");
3533
- await runOpenClawCommand({
3534
- openclawCommand,
3535
- args: [
3536
- "--profile",
3537
- params.profile,
3538
- "gateway",
3539
- "stop"
3540
- ],
3541
- timeoutMs: 9e4
3542
- }).catch(() => ({
3543
- code: 1,
3544
- stdout: "",
3545
- stderr: "stop timed out"
3546
- }));
3547
- s?.message("Installing gateway daemon…");
3530
+ s?.start("Refreshing gateway service definition…");
3548
3531
  await runOpenClawCommand({
3549
3532
  openclawCommand,
3550
3533
  args: [
@@ -3552,9 +3535,7 @@ async function restartGatewayDaemon(params) {
3552
3535
  params.profile,
3553
3536
  "gateway",
3554
3537
  "install",
3555
- "--force",
3556
- "--port",
3557
- String(params.gatewayPort)
3538
+ "--force"
3558
3539
  ],
3559
3540
  timeoutMs: 2 * 6e4
3560
3541
  }).catch(() => ({
@@ -3562,29 +3543,27 @@ async function restartGatewayDaemon(params) {
3562
3543
  stdout: "",
3563
3544
  stderr: "install failed"
3564
3545
  }));
3565
- s?.message("Starting gateway daemon…");
3566
- const startResult = await runOpenClawCommand({
3546
+ s?.message("Restarting gateway daemon…");
3547
+ const restartResult = await runOpenClawCommand({
3567
3548
  openclawCommand,
3568
3549
  args: [
3569
3550
  "--profile",
3570
3551
  params.profile,
3571
3552
  "gateway",
3572
- "start",
3573
- "--port",
3574
- String(params.gatewayPort)
3553
+ "restart"
3575
3554
  ],
3576
3555
  timeoutMs: 2 * 6e4
3577
3556
  }).catch(() => ({
3578
3557
  code: 1,
3579
3558
  stdout: "",
3580
- stderr: "start failed"
3559
+ stderr: "restart failed"
3581
3560
  }));
3582
- if (startResult.code !== 0) {
3583
- const detail = firstNonEmptyLine(startResult.stderr, startResult.stdout);
3561
+ if (restartResult.code !== 0) {
3562
+ const detail = firstNonEmptyLine(restartResult.stderr, restartResult.stdout);
3584
3563
  s?.stop(detail ? `Gateway restart failed: ${detail}` : "Gateway restart failed.");
3585
3564
  return {
3586
3565
  restarted: false,
3587
- error: detail ?? "gateway start failed"
3566
+ error: detail ?? "gateway restart failed"
3588
3567
  };
3589
3568
  }
3590
3569
  s?.stop("Gateway daemon restarted.");
@@ -220,7 +220,7 @@ async function runCli(argv = process$1.argv) {
220
220
  process$1.exitCode = await delegateToGlobalOpenClaw(parseArgv);
221
221
  return;
222
222
  }
223
- const { buildProgram } = await import("./program-DcrLV9Z2.js");
223
+ const { buildProgram } = await import("./program-D2RfLoKh.js");
224
224
  await buildProgram().parseAsync(parseArgv);
225
225
  }
226
226
 
@@ -0,0 +1,136 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { buildIdentityPrompt, resolveWorkspaceDir } from "./index.ts";
3
+ import register from "./index.ts";
4
+ import path from "node:path";
5
+
6
+ describe("buildIdentityPrompt", () => {
7
+ const workspaceDir = "/home/user/workspace";
8
+
9
+ it("includes chat history path so agent can reference past conversations", () => {
10
+ const prompt = buildIdentityPrompt(workspaceDir);
11
+ expect(prompt).toContain(".openclaw/web-chat/");
12
+ expect(prompt).toContain(
13
+ path.join(workspaceDir, ".openclaw/web-chat/"),
14
+ );
15
+ });
16
+
17
+ it("includes all workspace context paths (prevents agent losing orientation)", () => {
18
+ const prompt = buildIdentityPrompt(workspaceDir);
19
+ expect(prompt).toContain(`**Root**: \`${workspaceDir}\``);
20
+ expect(prompt).toContain(path.join(workspaceDir, "workspace.duckdb"));
21
+ expect(prompt).toContain(path.join(workspaceDir, "skills"));
22
+ expect(prompt).toContain(path.join(workspaceDir, "apps"));
23
+ });
24
+
25
+ it("includes CRM skill path for delegation (prevents agent using wrong skill path)", () => {
26
+ const prompt = buildIdentityPrompt(workspaceDir);
27
+ expect(prompt).toContain(
28
+ path.join(workspaceDir, "skills", "crm", "SKILL.md"),
29
+ );
30
+ });
31
+
32
+ it("includes exec approval policy (prevents agent stalling on exec confirmation)", () => {
33
+ const prompt = buildIdentityPrompt(workspaceDir);
34
+ expect(prompt).toContain("elevated: true");
35
+ expect(prompt).toContain("automatically approved");
36
+ });
37
+
38
+ it("references DenchClaw branding, not OpenClaw (prevents identity confusion)", () => {
39
+ const prompt = buildIdentityPrompt(workspaceDir);
40
+ expect(prompt).toContain("You are **DenchClaw**");
41
+ expect(prompt).toContain("always use **DenchClaw** (not OpenClaw)");
42
+ });
43
+ });
44
+
45
+ describe("resolveWorkspaceDir", () => {
46
+ it("returns workspace path when config is a valid string", () => {
47
+ const api = { config: { agents: { defaults: { workspace: "/home/user/ws" } } } };
48
+ expect(resolveWorkspaceDir(api)).toBe("/home/user/ws");
49
+ });
50
+
51
+ it("returns undefined when api is null (prevents crash on missing config)", () => {
52
+ expect(resolveWorkspaceDir(null)).toBeUndefined();
53
+ });
54
+
55
+ it("returns undefined when api is undefined (prevents crash on missing config)", () => {
56
+ expect(resolveWorkspaceDir(undefined)).toBeUndefined();
57
+ });
58
+
59
+ it("returns undefined when config chain is missing (prevents crash on partial config)", () => {
60
+ expect(resolveWorkspaceDir({})).toBeUndefined();
61
+ expect(resolveWorkspaceDir({ config: {} })).toBeUndefined();
62
+ expect(resolveWorkspaceDir({ config: { agents: {} } })).toBeUndefined();
63
+ expect(resolveWorkspaceDir({ config: { agents: { defaults: {} } } })).toBeUndefined();
64
+ });
65
+
66
+ it("returns undefined when workspace is empty string (prevents empty path injection)", () => {
67
+ const api = { config: { agents: { defaults: { workspace: "" } } } };
68
+ expect(resolveWorkspaceDir(api)).toBeUndefined();
69
+ });
70
+
71
+ it("returns undefined when workspace is whitespace-only (prevents whitespace path injection)", () => {
72
+ const api = { config: { agents: { defaults: { workspace: " " } } } };
73
+ expect(resolveWorkspaceDir(api)).toBeUndefined();
74
+ });
75
+
76
+ it("returns undefined when workspace is not a string (prevents type coercion)", () => {
77
+ expect(resolveWorkspaceDir({ config: { agents: { defaults: { workspace: 42 } } } })).toBeUndefined();
78
+ expect(resolveWorkspaceDir({ config: { agents: { defaults: { workspace: true } } } })).toBeUndefined();
79
+ expect(resolveWorkspaceDir({ config: { agents: { defaults: { workspace: null } } } })).toBeUndefined();
80
+ });
81
+
82
+ it("trims leading/trailing whitespace from valid paths", () => {
83
+ const api = { config: { agents: { defaults: { workspace: " /home/user/ws " } } } };
84
+ expect(resolveWorkspaceDir(api)).toBe("/home/user/ws");
85
+ });
86
+ });
87
+
88
+ describe("register", () => {
89
+ it("hooks into before_prompt_build event when enabled", () => {
90
+ const api = {
91
+ config: { plugins: { entries: {} }, agents: { defaults: { workspace: "/ws" } } },
92
+ on: vi.fn(),
93
+ };
94
+ register(api);
95
+ expect(api.on).toHaveBeenCalledWith(
96
+ "before_prompt_build",
97
+ expect.any(Function),
98
+ { priority: 100 },
99
+ );
100
+ });
101
+
102
+ it("does not register handler when plugin is explicitly disabled (respects config)", () => {
103
+ const api = {
104
+ config: { plugins: { entries: { "dench-identity": { config: { enabled: false } } } } },
105
+ on: vi.fn(),
106
+ };
107
+ register(api);
108
+ expect(api.on).not.toHaveBeenCalled();
109
+ });
110
+
111
+ it("handler returns system context when workspace is configured", () => {
112
+ const api = {
113
+ config: { plugins: { entries: {} }, agents: { defaults: { workspace: "/ws" } } },
114
+ on: vi.fn(),
115
+ };
116
+ register(api);
117
+
118
+ const handler = api.on.mock.calls[0][1];
119
+ const result = handler({}, {});
120
+ expect(result).toEqual({
121
+ prependSystemContext: expect.stringContaining("DenchClaw"),
122
+ });
123
+ });
124
+
125
+ it("handler returns undefined when workspace is not configured (prevents empty prompt)", () => {
126
+ const api = {
127
+ config: { plugins: { entries: {} }, agents: { defaults: {} } },
128
+ on: vi.fn(),
129
+ };
130
+ register(api);
131
+
132
+ const handler = api.on.mock.calls[0][1];
133
+ const result = handler({}, {});
134
+ expect(result).toBeUndefined();
135
+ });
136
+ });
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
 
3
3
  export const id = "dench-identity";
4
4
 
5
- function buildIdentityPrompt(workspaceDir: string): string {
5
+ export function buildIdentityPrompt(workspaceDir: string): string {
6
6
  const skillsDir = path.join(workspaceDir, "skills");
7
7
  const crmSkillPath = path.join(skillsDir, "crm", "SKILL.md");
8
8
  const browserSkillPath = path.join(skillsDir, "browser", "SKILL.md");
@@ -94,6 +94,7 @@ For multi-session projects, write a session handoff summary to \`${workspaceDir}
94
94
  - **Skills**: \`${skillsDir}\` — scan this directory for all available skills; new skills may be installed at any time
95
95
  - **Apps**: \`${appsDir}\` — \`.dench.app\` folders with \`.dench.yaml\` manifests
96
96
  - **Exec Approvals**: All commands are automatically approved by default. All exec calls must be elevated: true.
97
+ - **Chat History**: All chat history is stored in \`${workspaceDir}/.openclaw/web-chat/\`. This is where you can find all your past conversations and the context for the current session.
97
98
 
98
99
  ## Links
99
100
 
@@ -102,7 +103,7 @@ For multi-session projects, write a session handoff summary to \`${workspaceDir}
102
103
  - Skills Store: https://skills.sh`;
103
104
  }
104
105
 
105
- function resolveWorkspaceDir(api: any): string | undefined {
106
+ export function resolveWorkspaceDir(api: any): string | undefined {
106
107
  const ws = api?.config?.agents?.defaults?.workspace;
107
108
  return typeof ws === "string" ? ws.trim() || undefined : undefined;
108
109
  }
@@ -1,3 +1,3 @@
1
1
  export const POSTHOG_KEY = "phc_Nt8s1Ny8062TY9aKT8LWiB4nBuoHNYQXRU9MypWfQCW";
2
- export const DENCHCLAW_VERSION = "2.4.1";
2
+ export const DENCHCLAW_VERSION = "2.4.3";
3
3
  export const OPENCLAW_VERSION = "";
@@ -0,0 +1,13 @@
1
+ import baseConfig from "../vitest.config.ts";
2
+ import { defineConfig } from "vitest/config";
3
+
4
+ const base = baseConfig as unknown as Record<string, unknown>;
5
+ const baseTest = (baseConfig as { test?: Record<string, unknown> }).test ?? {};
6
+
7
+ export default defineConfig({
8
+ ...base,
9
+ test: {
10
+ ...baseTest,
11
+ include: ["extensions/**/*.test.ts"],
12
+ },
13
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "denchclaw",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "description": "Fully Managed OpenClaw Framework for managing your CRM, Sales Automation and Outreach agents. The only local productivity tool you need.",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/DenchHQ/DenchClaw#readme",
@@ -62,6 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@clack/prompts": "^1.0.1",
65
+ "axios": "^1.13.5",
65
66
  "chalk": "^5.6.2",
66
67
  "commander": "^14.0.3",
67
68
  "gradient-string": "^3.0.0",
@@ -95,6 +95,51 @@ Never rename partially. If you can't complete all steps, don't start the rename
95
95
  - **DuckDB SQL errors**: See the "Common DuckDB Pitfalls" section in the **duckdb-operations** child skill. The most frequent causes: unquoted field names with spaces (use `"Full Name"` not `Full Name`), wrong transaction syntax (`BEGIN TRANSACTION` not `BEGIN`), unescaped single quotes, and PIVOT views without the `IN (...)` field list.
96
96
  - **"Script not found" on action buttons**: The `.actions/` directory and/or script files were not created. See the **actions** child skill post-creation checklist.
97
97
 
98
+ ## Relation Fields — Link Objects Automatically
99
+
100
+ **Every time you create or modify objects, check whether relation fields should connect them. Do NOT wait for the user to ask — proactively link objects that are obviously related.**
101
+
102
+ Relation fields (`type: "relation"`) let an entry in one object reference an entry in another. They render as a searchable dropdown in the UI and are the backbone of any useful CRM.
103
+
104
+ ### Mandatory auto-linking rules
105
+
106
+ When you create an object, scan all existing objects (run `SELECT name FROM objects`) and add relation fields for every obvious link:
107
+
108
+ | If you're creating... | And this object exists... | Auto-create this relation field |
109
+ |---|---|---|
110
+ | people / contact | company | "Company" on people → company (`many_to_one`) |
111
+ | lead / prospect | company | "Company" on lead → company (`many_to_one`) |
112
+ | deal / opportunity | people | "Primary Contact" on deal → people (`many_to_one`) |
113
+ | deal / opportunity | company | "Company" on deal → company (`many_to_one`) |
114
+ | task | people | "Assigned Contact" on task → people (`many_to_one`) |
115
+ | task | project / deal | "Related To" on task → parent (`many_to_one`) |
116
+ | case | people or company | "Client" on case → people/company (`many_to_one`) |
117
+ | invoice / payment | company | "Company" on invoice → company (`many_to_one`) |
118
+ | invoice / payment | deal | "Deal" on invoice → deal (`many_to_one`) |
119
+ | property / listing | people | "Agent" on property → people (`many_to_one`) |
120
+ | any child concept | its parent concept | relation to parent (`many_to_one`) |
121
+
122
+ **Use this SQL pattern** (safe — does nothing if the target object doesn't exist):
123
+
124
+ ```sql
125
+ INSERT INTO fields (object_id, name, type, related_object_id, relationship_type, sort_order)
126
+ SELECT
127
+ (SELECT id FROM objects WHERE name = 'people'),
128
+ 'Company',
129
+ 'relation',
130
+ (SELECT id FROM objects WHERE name = 'company'),
131
+ 'many_to_one',
132
+ 3
133
+ WHERE EXISTS (SELECT 1 FROM objects WHERE name = 'company')
134
+ ON CONFLICT (object_id, name) DO NOTHING;
135
+ ```
136
+
137
+ **General rule**: If you're creating object B and object A already exists, ask: "Would an entry in B logically belong to or reference an entry in A?" If yes, add a `many_to_one` relation. Use `many_to_many` only when an entry can link to multiple entries (e.g. project → team members).
138
+
139
+ Relation fields **must be created via SQL** — the API does not support the `relation` type.
140
+
141
+ ---
142
+
98
143
  ## Critical Reminders
99
144
 
100
145
  - Handle the ENTIRE CRM operation from analysis to SQL execution to filesystem projection to summary
@@ -111,7 +156,7 @@ Never rename partially. If you can't complete all steps, don't start the rename
111
156
  - **ENTRY DOCUMENTS**: New entry detail pages should use human-readable filenames (for example `yt-mikemurphy-001.md`, `jane-smith-001.md`, `acme-corp-001.md`) and MUST be registered in DuckDB `documents` with `file_path`, `parent_object_id`, and `entry_id`. Do NOT default to raw `{entry_id}.md` filenames for new docs. See **documents** child skill.
112
157
  - **USER FIELDS**: Resolve member name to ID from `workspace_context.yaml` BEFORE inserting
113
158
  - **ENUM FIELDS**: Use type "enum" with `enum_values` JSON array
114
- - **RELATION FIELDS (PROACTIVE)**: Use type "relation" with `related_object_id` and `relationship_type`. **ALWAYS create relation fields when objects are obviously linked** — don't wait for the user to ask. Examples: people→company, deal→contact, deal→company, task→project, case→client, invoice→company. If you're creating object B and object A exists, ask: "Would B entries reference A entries?" If yes, add a relation. Relation fields must be created via SQL (not the API). See **object-builder** child skill for the full relation pattern and common relation pairs.
159
+ - **RELATION FIELDS (PROACTIVE SEE SECTION ABOVE)**: ALWAYS create relation fields when objects are obviously linked — don't wait for the user to ask. See the **"Relation Fields Link Objects Automatically"** section above for the full table of mandatory auto-linking rules and the SQL pattern.
115
160
  - **TAGS FIELDS**: Use type "tags" for free-form string arrays. Value stored as `'["tag1","tag2"]'`
116
161
  - **URL FIELDS**: Use type "url" for website addresses and links
117
162
  - **FILE FIELDS**: Use type "file" for file attachments (stores file path or URL)