denchclaw 2.0.7 → 2.0.9
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/apps/web/.next/standalone/apps/web/.next/BUILD_ID +1 -1
- package/apps/web/.next/standalone/apps/web/.next/app-build-manifest.json +149 -148
- package/apps/web/.next/standalone/apps/web/.next/app-path-routes-manifest.json +24 -24
- package/apps/web/.next/standalone/apps/web/.next/build-manifest.json +5 -5
- package/apps/web/.next/standalone/apps/web/.next/react-loadable-manifest.json +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found/page.js +2 -2
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/_not-found.rsc +2 -2
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/active/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stop/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stop/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/stream/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/chat/subagents/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/jobs/[jobId]/runs/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/jobs/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/runs/[sessionId]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/cron/runs/search-transcript/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/feedback/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/feedback/route.js.nft.json +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/feedback/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/memories/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/profiles/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/profiles/switch/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/sessions/[sessionId]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/[id]/messages/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/[id]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/web-sessions/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/assets/[...path]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/browse/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/browse-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/context/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/copy/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/db/introspect/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/db/query/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/delete/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/delete/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/init/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/init/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/list/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/mkdir/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/move/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/display-field/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/[id]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/bulk-delete/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/options/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/entries/route_client-reference-manifest.js +1 -1
- 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
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/[fieldId]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/fields/reorder/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/objects/[name]/views/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/open-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/path-info/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/query/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/raw-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/rename/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/reports/execute/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/reports/execute/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/search-index/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/suggest-files/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/switch/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/switch/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/thumbnail/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/tree/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/upload/route.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/upload/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/virtual-file/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/watch/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/api/workspace/write-binary/route_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/index.html +1 -72
- package/apps/web/.next/standalone/apps/web/.next/server/app/index.rsc +2 -2
- package/apps/web/.next/standalone/apps/web/.next/server/app/page.js +2 -2
- package/apps/web/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace/page.js +10 -10
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace/page.js.nft.json +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace/page_client-reference-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace.html +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/app/workspace.rsc +3 -3
- package/apps/web/.next/standalone/apps/web/.next/server/app-paths-manifest.json +24 -24
- package/apps/web/.next/standalone/apps/web/.next/server/chunks/5334.js +9 -0
- package/apps/web/.next/standalone/apps/web/.next/server/functions-config-manifest.json +11 -10
- package/apps/web/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
- package/apps/web/.next/{static/chunks/9978-a2bf90231d08284f.js → standalone/apps/web/.next/static/chunks/4988-a31fb680255d2e53.js} +3 -3
- package/apps/web/.next/{static/chunks/7597.6cb599051f1a46f7.js → standalone/apps/web/.next/static/chunks/9978.7ad588863427f502.js} +1 -1
- package/apps/web/.next/standalone/apps/web/.next/static/chunks/app/layout-f0561292a97b3d54.js +1 -0
- package/apps/web/.next/standalone/apps/web/.next/static/chunks/app/workspace/{page-fe39ceef01137175.js → page-c589ce6d59e620cf.js} +1 -1
- package/apps/web/.next/standalone/apps/web/.next/static/chunks/df90ee31-b8ee23eaa0135739.js +1 -0
- package/apps/web/.next/standalone/apps/web/.next/static/chunks/{webpack-1032d39338965700.js → webpack-38d8ef58f812737b.js} +1 -1
- package/apps/web/.next/standalone/apps/web/public/fonts/Bookerly-Bold.ttf +0 -0
- package/apps/web/.next/standalone/apps/web/public/fonts/Bookerly-BoldItalic.ttf +0 -0
- package/apps/web/.next/standalone/apps/web/public/fonts/Bookerly-Regular.ttf +0 -0
- package/apps/web/.next/standalone/apps/web/public/fonts/Bookerly-RegularItalic.ttf +0 -0
- package/apps/web/.next/standalone/package.json +1 -1
- package/apps/web/.next/{standalone/apps/web/.next/static/chunks/9978-a2bf90231d08284f.js → static/chunks/4988-a31fb680255d2e53.js} +3 -3
- package/apps/web/.next/{standalone/apps/web/.next/static/chunks/7597.6cb599051f1a46f7.js → static/chunks/9978.7ad588863427f502.js} +1 -1
- package/apps/web/.next/static/chunks/app/layout-f0561292a97b3d54.js +1 -0
- package/apps/web/.next/static/chunks/app/workspace/{page-fe39ceef01137175.js → page-c589ce6d59e620cf.js} +1 -1
- package/apps/web/.next/static/chunks/df90ee31-b8ee23eaa0135739.js +1 -0
- package/apps/web/.next/static/chunks/{webpack-1032d39338965700.js → webpack-38d8ef58f812737b.js} +1 -1
- package/apps/web/public/fonts/Bookerly-Bold.ttf +0 -0
- package/apps/web/public/fonts/Bookerly-BoldItalic.ttf +0 -0
- package/apps/web/public/fonts/Bookerly-Regular.ttf +0 -0
- package/apps/web/public/fonts/Bookerly-RegularItalic.ttf +0 -0
- package/dist/entry.js +1 -1
- package/dist/{program-CfNvnqVx.js → program-BYwTE_Ot.js} +17 -4
- package/dist/{run-main-BWHpVYis.js → run-main-CWe8UBRN.js} +1 -1
- package/extensions/posthog-analytics/index.ts +1 -1
- package/extensions/posthog-analytics/lib/event-mappers.ts +89 -10
- package/extensions/posthog-analytics/lib/privacy.ts +135 -0
- package/package.json +1 -1
- package/apps/web/.next/standalone/apps/web/.next/server/chunks/3180.js +0 -9
- package/apps/web/.next/standalone/apps/web/.next/static/chunks/app/layout-af0e9292b7fe2ec1.js +0 -1
- package/apps/web/.next/standalone/apps/web/.next/static/chunks/df90ee31-33cbf7968acd3b6e.js +0 -1
- package/apps/web/.next/static/chunks/app/layout-af0e9292b7fe2ec1.js +0 -1
- package/apps/web/.next/static/chunks/df90ee31-33cbf7968acd3b6e.js +0 -1
- /package/apps/web/.next/standalone/apps/web/.next/static/{ezaYRoo0uHfvCgSL72gWA → MgHqcQXhG7LZyx8fD3wsG}/_buildManifest.js +0 -0
- /package/apps/web/.next/standalone/apps/web/.next/static/{ezaYRoo0uHfvCgSL72gWA → MgHqcQXhG7LZyx8fD3wsG}/_ssgManifest.js +0 -0
- /package/apps/web/.next/static/{ezaYRoo0uHfvCgSL72gWA → MgHqcQXhG7LZyx8fD3wsG}/_buildManifest.js +0 -0
- /package/apps/web/.next/static/{ezaYRoo0uHfvCgSL72gWA → MgHqcQXhG7LZyx8fD3wsG}/_ssgManifest.js +0 -0
|
@@ -1342,6 +1342,19 @@ async function installBundledPlugins(params) {
|
|
|
1342
1342
|
errorMessage: "Failed to set posthog-analytics API key."
|
|
1343
1343
|
});
|
|
1344
1344
|
}
|
|
1345
|
+
try {
|
|
1346
|
+
await runOpenClawOrThrow({
|
|
1347
|
+
openclawCommand: params.openclawCommand,
|
|
1348
|
+
args: [
|
|
1349
|
+
"--profile",
|
|
1350
|
+
params.profile,
|
|
1351
|
+
"gateway",
|
|
1352
|
+
"restart"
|
|
1353
|
+
],
|
|
1354
|
+
timeoutMs: 6e4,
|
|
1355
|
+
errorMessage: "Failed to restart gateway after plugin install."
|
|
1356
|
+
});
|
|
1357
|
+
} catch {}
|
|
1345
1358
|
return true;
|
|
1346
1359
|
} catch {
|
|
1347
1360
|
return false;
|
|
@@ -2824,7 +2837,7 @@ function registerTelemetryCommand(program) {
|
|
|
2824
2837
|
cmd.command("disable").description("Disable anonymous telemetry").action(() => {
|
|
2825
2838
|
writeTelemetryConfig({ enabled: false });
|
|
2826
2839
|
console.log("Telemetry has been disabled.");
|
|
2827
|
-
console.log("You can re-enable it anytime with: denchclaw telemetry enable");
|
|
2840
|
+
console.log("You can re-enable it anytime with: npx denchclaw telemetry enable");
|
|
2828
2841
|
});
|
|
2829
2842
|
cmd.command("enable").description("Enable anonymous telemetry").action(() => {
|
|
2830
2843
|
writeTelemetryConfig({ enabled: true });
|
|
@@ -2833,7 +2846,7 @@ function registerTelemetryCommand(program) {
|
|
|
2833
2846
|
const privacyCmd = cmd.command("privacy").description("Control whether message content is included in telemetry");
|
|
2834
2847
|
privacyCmd.command("on").description("Enable privacy mode (redacts message content, default)").action(() => {
|
|
2835
2848
|
if (!isTelemetryEnabled()) {
|
|
2836
|
-
console.log("Telemetry is currently disabled. Enable it first with: denchclaw telemetry enable");
|
|
2849
|
+
console.log("Telemetry is currently disabled. Enable it first with: npx denchclaw telemetry enable");
|
|
2837
2850
|
return;
|
|
2838
2851
|
}
|
|
2839
2852
|
writeTelemetryConfig({ privacyMode: true });
|
|
@@ -2841,12 +2854,12 @@ function registerTelemetryCommand(program) {
|
|
|
2841
2854
|
});
|
|
2842
2855
|
privacyCmd.command("off").description("Disable privacy mode (sends full message content)").action(() => {
|
|
2843
2856
|
if (!isTelemetryEnabled()) {
|
|
2844
|
-
console.log("Telemetry is currently disabled. Enable it first with: denchclaw telemetry enable");
|
|
2857
|
+
console.log("Telemetry is currently disabled. Enable it first with: npx denchclaw telemetry enable");
|
|
2845
2858
|
return;
|
|
2846
2859
|
}
|
|
2847
2860
|
writeTelemetryConfig({ privacyMode: false });
|
|
2848
2861
|
console.log("Privacy mode disabled. Full message content and tool results will be captured.");
|
|
2849
|
-
console.log("Re-enable anytime with: denchclaw telemetry privacy on");
|
|
2862
|
+
console.log("Re-enable anytime with: npx denchclaw telemetry privacy on");
|
|
2850
2863
|
});
|
|
2851
2864
|
}
|
|
2852
2865
|
|
|
@@ -219,7 +219,7 @@ async function runCli(argv = process$1.argv) {
|
|
|
219
219
|
process$1.exitCode = await delegateToGlobalOpenClaw(parseArgv);
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
222
|
-
const { buildProgram } = await import("./program-
|
|
222
|
+
const { buildProgram } = await import("./program-BYwTE_Ot.js");
|
|
223
223
|
await buildProgram().parseAsync(parseArgv);
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -109,7 +109,7 @@ export default function register(api: any) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
emitGeneration(ph, traceCtx, sk, event, getPrivacyMode());
|
|
112
|
-
emitTrace(ph, traceCtx, sk);
|
|
112
|
+
emitTrace(ph, traceCtx, sk, event, getPrivacyMode());
|
|
113
113
|
emitCustomEvent(ph, "dench_turn_completed", {
|
|
114
114
|
session_id: sk,
|
|
115
115
|
run_id: ctx.runId,
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import type { PostHogClient } from "./posthog-client.js";
|
|
4
4
|
import type { TraceContextManager } from "./trace-context.js";
|
|
5
|
-
import {
|
|
5
|
+
import { sanitizeMessages, sanitizeOutputChoices, stripSecrets } from "./privacy.js";
|
|
6
6
|
|
|
7
7
|
function getAnonymousId(): string {
|
|
8
8
|
try {
|
|
@@ -15,8 +15,6 @@ function getAnonymousId(): string {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Extract actual token counts and cost from OpenClaw's per-message usage metadata.
|
|
18
|
-
* Each assistant message in the agent_end messages array includes:
|
|
19
|
-
* usage: { input, output, cacheRead, cacheWrite, cost: { total, input, output, ... } }
|
|
20
18
|
*/
|
|
21
19
|
export function extractUsageFromMessages(messages: unknown): {
|
|
22
20
|
inputTokens: number;
|
|
@@ -52,25 +50,25 @@ export function extractToolNamesFromMessages(messages: unknown): string[] {
|
|
|
52
50
|
if (!msg || typeof msg !== "object") continue;
|
|
53
51
|
const m = msg as Record<string, unknown>;
|
|
54
52
|
|
|
55
|
-
// OpenAI format: assistant message with tool_calls array
|
|
56
53
|
if (Array.isArray(m.tool_calls)) {
|
|
57
54
|
for (const tc of m.tool_calls) {
|
|
58
55
|
const name = (tc as any)?.function?.name ?? (tc as any)?.name;
|
|
59
56
|
if (typeof name === "string" && name) names.push(name);
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
|
-
// Anthropic format: content blocks with type "tool_use"
|
|
63
59
|
if (Array.isArray(m.content)) {
|
|
64
60
|
for (const block of m.content) {
|
|
65
61
|
if ((block as any)?.type === "tool_use" && typeof (block as any)?.name === "string") {
|
|
66
62
|
names.push((block as any).name);
|
|
67
63
|
}
|
|
64
|
+
if ((block as any)?.type === "toolCall" && typeof (block as any)?.name === "string") {
|
|
65
|
+
names.push((block as any).name);
|
|
66
|
+
}
|
|
68
67
|
if ((block as any)?.type === "tool-call" && typeof (block as any)?.toolName === "string") {
|
|
69
68
|
names.push((block as any).toolName);
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
|
-
// Tool result messages have a "name" field with the tool name
|
|
74
72
|
if (m.role === "tool" && typeof m.name === "string") {
|
|
75
73
|
names.push(m.name);
|
|
76
74
|
}
|
|
@@ -81,8 +79,6 @@ export function extractToolNamesFromMessages(messages: unknown): string[] {
|
|
|
81
79
|
/**
|
|
82
80
|
* Normalize OpenClaw's message format into OpenAI-compatible output choices
|
|
83
81
|
* so PostHog can extract tool calls for the Tools tab.
|
|
84
|
-
* Converts Anthropic-style content blocks (type: "toolCall") to OpenAI's
|
|
85
|
-
* tool_calls array format.
|
|
86
82
|
*/
|
|
87
83
|
export function normalizeOutputForPostHog(messages: unknown): unknown[] | undefined {
|
|
88
84
|
if (!Array.isArray(messages)) return undefined;
|
|
@@ -135,6 +131,80 @@ export function normalizeOutputForPostHog(messages: unknown): unknown[] | undefi
|
|
|
135
131
|
return choices.length > 0 ? choices : undefined;
|
|
136
132
|
}
|
|
137
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Build full conversation state for the $ai_trace event.
|
|
136
|
+
* Splits messages into input (user/tool/system) and output (assistant) arrays,
|
|
137
|
+
* preserving chronological order so PostHog renders the full conversation.
|
|
138
|
+
*/
|
|
139
|
+
export function buildTraceState(
|
|
140
|
+
messages: unknown,
|
|
141
|
+
privacyMode: boolean,
|
|
142
|
+
): { inputState: unknown; outputState: unknown } {
|
|
143
|
+
if (!Array.isArray(messages)) return { inputState: undefined, outputState: undefined };
|
|
144
|
+
|
|
145
|
+
const inputMessages: unknown[] = [];
|
|
146
|
+
const outputMessages: unknown[] = [];
|
|
147
|
+
|
|
148
|
+
for (const msg of messages) {
|
|
149
|
+
if (!msg || typeof msg !== "object") continue;
|
|
150
|
+
const m = msg as Record<string, unknown>;
|
|
151
|
+
|
|
152
|
+
const extractText = () => {
|
|
153
|
+
if (Array.isArray(m.content)) {
|
|
154
|
+
return (m.content as Array<Record<string, unknown>>)
|
|
155
|
+
.filter((b) => b.type === "text")
|
|
156
|
+
.map((b) => b.text)
|
|
157
|
+
.join("");
|
|
158
|
+
}
|
|
159
|
+
return typeof m.content === "string" ? m.content : null;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
if (m.role === "assistant") {
|
|
163
|
+
const content = privacyMode ? "[REDACTED]" : extractText();
|
|
164
|
+
const entry: Record<string, unknown> = { role: "assistant", content };
|
|
165
|
+
|
|
166
|
+
const toolNames = extractToolNamesFromSingleMessage(m);
|
|
167
|
+
if (toolNames.length > 0) {
|
|
168
|
+
entry.tool_calls = toolNames.map((name) => ({
|
|
169
|
+
type: "function",
|
|
170
|
+
function: { name },
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
outputMessages.push(entry);
|
|
175
|
+
} else if (m.role === "user" || m.role === "tool" || m.role === "toolResult" || m.role === "system") {
|
|
176
|
+
const content = privacyMode ? "[REDACTED]" : extractText();
|
|
177
|
+
const entry: Record<string, unknown> = { role: m.role, content };
|
|
178
|
+
if (m.name) entry.name = m.name;
|
|
179
|
+
if (m.toolName) entry.toolName = m.toolName;
|
|
180
|
+
inputMessages.push(entry);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
inputState: inputMessages.length > 0 ? inputMessages : undefined,
|
|
186
|
+
outputState: outputMessages.length > 0 ? outputMessages : undefined,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function extractToolNamesFromSingleMessage(m: Record<string, unknown>): string[] {
|
|
191
|
+
const names: string[] = [];
|
|
192
|
+
if (Array.isArray(m.tool_calls)) {
|
|
193
|
+
for (const tc of m.tool_calls) {
|
|
194
|
+
const name = (tc as any)?.function?.name ?? (tc as any)?.name;
|
|
195
|
+
if (typeof name === "string" && name) names.push(name);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (Array.isArray(m.content)) {
|
|
199
|
+
for (const block of m.content) {
|
|
200
|
+
if ((block as any)?.type === "toolCall" && typeof (block as any)?.name === "string") {
|
|
201
|
+
names.push((block as any).name);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return names;
|
|
206
|
+
}
|
|
207
|
+
|
|
138
208
|
/**
|
|
139
209
|
* Emit a `$ai_generation` event from the agent_end hook data.
|
|
140
210
|
*/
|
|
@@ -187,10 +257,10 @@ export function emitGeneration(
|
|
|
187
257
|
if (extracted.totalCostUsd > 0) properties.$ai_total_cost_usd = extracted.totalCostUsd;
|
|
188
258
|
}
|
|
189
259
|
|
|
190
|
-
properties.$ai_input =
|
|
260
|
+
properties.$ai_input = sanitizeMessages(trace.input, privacyMode);
|
|
191
261
|
|
|
192
262
|
const outputChoices = normalizeOutputForPostHog(event.messages);
|
|
193
|
-
properties.$ai_output_choices =
|
|
263
|
+
properties.$ai_output_choices = sanitizeOutputChoices(
|
|
194
264
|
outputChoices ?? event.output ?? event.messages,
|
|
195
265
|
privacyMode,
|
|
196
266
|
);
|
|
@@ -264,6 +334,8 @@ export function emitTrace(
|
|
|
264
334
|
ph: PostHogClient,
|
|
265
335
|
traceCtx: TraceContextManager,
|
|
266
336
|
sessionKey: string,
|
|
337
|
+
event?: any,
|
|
338
|
+
privacyMode?: boolean,
|
|
267
339
|
): void {
|
|
268
340
|
try {
|
|
269
341
|
const trace = traceCtx.getTrace(sessionKey);
|
|
@@ -273,6 +345,11 @@ export function emitTrace(
|
|
|
273
345
|
? (Date.now() - trace.startedAt) / 1_000
|
|
274
346
|
: undefined;
|
|
275
347
|
|
|
348
|
+
const { inputState, outputState } = buildTraceState(
|
|
349
|
+
event?.messages,
|
|
350
|
+
privacyMode ?? true,
|
|
351
|
+
);
|
|
352
|
+
|
|
276
353
|
ph.capture({
|
|
277
354
|
distinctId: getAnonymousId(),
|
|
278
355
|
event: "$ai_trace",
|
|
@@ -281,6 +358,8 @@ export function emitTrace(
|
|
|
281
358
|
$ai_session_id: trace.sessionId,
|
|
282
359
|
$ai_latency: latency,
|
|
283
360
|
$ai_span_name: "agent_run",
|
|
361
|
+
$ai_input_state: inputState,
|
|
362
|
+
$ai_output_state: outputState,
|
|
284
363
|
tool_count: trace.toolSpans.length,
|
|
285
364
|
},
|
|
286
365
|
});
|
|
@@ -81,3 +81,138 @@ export function sanitizeForCapture(
|
|
|
81
81
|
if (privacyMode) return REDACTED;
|
|
82
82
|
return stripSecrets(value);
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Redact a tool_calls array while preserving tool name, id, and type metadata.
|
|
87
|
+
* Only arguments are redacted.
|
|
88
|
+
*/
|
|
89
|
+
function redactToolCalls(toolCalls: unknown[]): unknown[] {
|
|
90
|
+
return toolCalls.map((tc: any) => {
|
|
91
|
+
if (!tc || typeof tc !== "object") return tc;
|
|
92
|
+
const out: Record<string, unknown> = {
|
|
93
|
+
id: tc.id,
|
|
94
|
+
type: tc.type ?? "function",
|
|
95
|
+
};
|
|
96
|
+
if (tc.function && typeof tc.function === "object") {
|
|
97
|
+
out.function = {
|
|
98
|
+
name: tc.function.name,
|
|
99
|
+
arguments: REDACTED,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (tc.name) out.name = tc.name;
|
|
103
|
+
return out;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Redact Anthropic-format content blocks while preserving tool metadata.
|
|
109
|
+
* Text blocks get redacted; toolCall blocks keep their name but redact arguments.
|
|
110
|
+
*/
|
|
111
|
+
function redactContentBlocks(blocks: unknown[]): unknown[] {
|
|
112
|
+
return blocks.map((block: any) => {
|
|
113
|
+
if (!block || typeof block !== "object") return block;
|
|
114
|
+
if (block.type === "text") {
|
|
115
|
+
return { type: "text", text: REDACTED };
|
|
116
|
+
}
|
|
117
|
+
if (block.type === "toolCall") {
|
|
118
|
+
return {
|
|
119
|
+
type: "toolCall",
|
|
120
|
+
id: block.id ?? block.toolCallId,
|
|
121
|
+
name: block.name,
|
|
122
|
+
arguments: REDACTED,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (block.type === "tool_use") {
|
|
126
|
+
return {
|
|
127
|
+
type: "tool_use",
|
|
128
|
+
id: block.id,
|
|
129
|
+
name: block.name,
|
|
130
|
+
input: REDACTED,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (block.type === "thinking") {
|
|
134
|
+
return { type: "thinking", text: REDACTED };
|
|
135
|
+
}
|
|
136
|
+
return { type: block.type };
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Structure-preserving message redaction for PostHog message-array fields.
|
|
142
|
+
* Preserves: role, tool names, tool_call IDs, message ordering, tool types.
|
|
143
|
+
* Redacts: text content, tool arguments, tool results.
|
|
144
|
+
*/
|
|
145
|
+
export function redactMessagesStructured(messages: unknown): unknown {
|
|
146
|
+
if (!Array.isArray(messages)) return messages;
|
|
147
|
+
return messages.map((msg: any) => {
|
|
148
|
+
if (!msg || typeof msg !== "object") return msg;
|
|
149
|
+
const out: Record<string, unknown> = { role: msg.role };
|
|
150
|
+
|
|
151
|
+
if (msg.name) out.name = msg.name;
|
|
152
|
+
if (msg.tool_call_id) out.tool_call_id = msg.tool_call_id;
|
|
153
|
+
if (msg.toolCallId) out.toolCallId = msg.toolCallId;
|
|
154
|
+
if (msg.toolName) out.toolName = msg.toolName;
|
|
155
|
+
if (msg.isError != null) out.isError = msg.isError;
|
|
156
|
+
if (msg.stopReason) out.stopReason = msg.stopReason;
|
|
157
|
+
if (msg.model) out.model = msg.model;
|
|
158
|
+
if (msg.provider) out.provider = msg.provider;
|
|
159
|
+
|
|
160
|
+
if (Array.isArray(msg.content)) {
|
|
161
|
+
out.content = redactContentBlocks(msg.content);
|
|
162
|
+
} else {
|
|
163
|
+
out.content = REDACTED;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (Array.isArray(msg.tool_calls)) {
|
|
167
|
+
out.tool_calls = redactToolCalls(msg.tool_calls);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return out;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Structure-preserving redaction for normalized OpenAI-format output choices.
|
|
176
|
+
* Keeps role, tool_calls[].function.name, tool_calls[].type.
|
|
177
|
+
* Redacts text content and tool arguments.
|
|
178
|
+
*/
|
|
179
|
+
export function redactOutputChoicesStructured(choices: unknown): unknown {
|
|
180
|
+
if (!Array.isArray(choices)) return choices;
|
|
181
|
+
return choices.map((choice: any) => {
|
|
182
|
+
if (!choice || typeof choice !== "object") return choice;
|
|
183
|
+
const out: Record<string, unknown> = {
|
|
184
|
+
role: choice.role,
|
|
185
|
+
content: choice.content != null ? REDACTED : null,
|
|
186
|
+
};
|
|
187
|
+
if (Array.isArray(choice.tool_calls)) {
|
|
188
|
+
out.tool_calls = redactToolCalls(choice.tool_calls);
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Sanitize messages for PostHog capture, preserving structure in privacy mode.
|
|
196
|
+
* Privacy on: redacts text content/arguments but keeps role, tool names, ordering.
|
|
197
|
+
* Privacy off: only strips credential patterns.
|
|
198
|
+
*/
|
|
199
|
+
export function sanitizeMessages(
|
|
200
|
+
messages: unknown,
|
|
201
|
+
privacyMode: boolean,
|
|
202
|
+
): unknown {
|
|
203
|
+
if (privacyMode) return redactMessagesStructured(messages);
|
|
204
|
+
return stripSecrets(messages);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Sanitize normalized output choices for PostHog capture, preserving structure.
|
|
209
|
+
* Privacy on: redacts text content/arguments but keeps role, tool names.
|
|
210
|
+
* Privacy off: only strips credential patterns.
|
|
211
|
+
*/
|
|
212
|
+
export function sanitizeOutputChoices(
|
|
213
|
+
choices: unknown,
|
|
214
|
+
privacyMode: boolean,
|
|
215
|
+
): unknown {
|
|
216
|
+
if (privacyMode) return redactOutputChoicesStructured(choices);
|
|
217
|
+
return stripSecrets(choices);
|
|
218
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "denchclaw",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
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",
|