behavior-wrapped 0.8.9 → 0.8.11
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/dist/assets/index-CFnO0oEF.js +11 -0
- package/dist/assets/{index-RUb7reCR.css → index-Ctd7RCMR.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/analysis.mjs +11 -7
- package/server/privacy.mjs +5 -3
- package/server/public-report-schema.mjs +1 -1
- package/dist/assets/index-CD48EtI2.js +0 -11
package/dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<meta name="theme-color" content="#0d0b1b" />
|
|
7
7
|
<meta name="description" content="A local-first behavior report for you and your AI agents." />
|
|
8
8
|
<title>Behavior Wrapped</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-CFnO0oEF.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Ctd7RCMR.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/package.json
CHANGED
package/server/analysis.mjs
CHANGED
|
@@ -62,7 +62,7 @@ function proseText(value) {
|
|
|
62
62
|
.replace(/`[^`\n]+`/g, " ")
|
|
63
63
|
.replace(/^\s*>.*$/gm, " ")
|
|
64
64
|
.replace(/https?:\/\/\S+/g, " ")
|
|
65
|
-
.replace(
|
|
65
|
+
.replace(/(?:^|\s)(?:[A-Za-z]:)?[/.~][^\s]+/g, " ")
|
|
66
66
|
.replace(/<[^>]+>/g, " ")
|
|
67
67
|
.replace(/\s+/g, " ")
|
|
68
68
|
.trim();
|
|
@@ -87,9 +87,10 @@ function repeatedInstructions(sessionRecords) {
|
|
|
87
87
|
for (let sessionIndex = 0; sessionIndex < sessionRecords.length; sessionIndex++) {
|
|
88
88
|
for (const record of sessionRecords[sessionIndex].records) {
|
|
89
89
|
if (record.type !== "user" || record.isMeta) continue;
|
|
90
|
-
const cleaned = redactAggregateText(proseText(visibleText(record)));
|
|
90
|
+
const cleaned = redactAggregateText(proseText(localOpeningPrompt(visibleText(record))));
|
|
91
91
|
if (!cleaned || /\[(?:REDACTED|REMOVED)[^\]]*\]/i.test(cleaned)) continue;
|
|
92
92
|
for (const rawClause of cleaned.split(/(?<=[.!?])\s+|[;\n]+/)) {
|
|
93
|
+
if (/\*\*[^*\n]{2,80}:\*\*/.test(rawClause)) continue;
|
|
93
94
|
const instruction = rawClause.replace(/^[-*\d.)\s]+/, "").replace(/\s+/g, " ").trim();
|
|
94
95
|
const words = instruction.match(/\p{L}+(?:['’]\p{L}+)?/gu) || [];
|
|
95
96
|
if (words.length < 3 || words.length > 18 || instruction.length > 160 || !instructionOpeningPattern.test(instruction)) continue;
|
|
@@ -106,7 +107,7 @@ function repeatedInstructions(sessionRecords) {
|
|
|
106
107
|
return [...instructions.values()]
|
|
107
108
|
.filter((item) => item.occurrences >= 2)
|
|
108
109
|
.sort((left, right) => right.sessions.size - left.sessions.size || right.occurrences - left.occurrences || left.instruction.localeCompare(right.instruction))
|
|
109
|
-
.slice(0,
|
|
110
|
+
.slice(0, 2)
|
|
110
111
|
.map((item) => ({ instruction: item.instruction, occurrences: item.occurrences, distinctSessions: item.sessions.size }));
|
|
111
112
|
}
|
|
112
113
|
|
|
@@ -453,6 +454,8 @@ export function analyzeSessions(sessionRecords) {
|
|
|
453
454
|
};
|
|
454
455
|
for (const [recordIndex, record] of records.entries()) {
|
|
455
456
|
const text = visibleText(record);
|
|
457
|
+
const userProse = record.type === "user" ? proseText(localOpeningPrompt(text)) : "";
|
|
458
|
+
const assistantResponseProse = record.type === "assistant" ? proseText(text) : "";
|
|
456
459
|
const declaredModel = record?.message?.model || record?.model;
|
|
457
460
|
if (typeof declaredModel === "string" && declaredModel) currentModel = declaredModel;
|
|
458
461
|
if (record.type === "system" && record.subtype === "turn_duration") {
|
|
@@ -462,10 +465,10 @@ export function analyzeSessions(sessionRecords) {
|
|
|
462
465
|
longestUninterruptedRun = { durationMs: Math.round(durationMs), agent, agentName: definition?.name || "Agent" };
|
|
463
466
|
}
|
|
464
467
|
}
|
|
465
|
-
if (record.type === "user" && !record.isMeta &&
|
|
468
|
+
if (record.type === "user" && !record.isMeta && userProse) {
|
|
466
469
|
finishResponse();
|
|
467
470
|
hasCurrentPrompt = true;
|
|
468
|
-
userInputWords += wordCount(
|
|
471
|
+
userInputWords += wordCount(userProse);
|
|
469
472
|
userInputCount++;
|
|
470
473
|
sessionTurns++;
|
|
471
474
|
if (isFrustratedMessage(text)) frustratedMessages++;
|
|
@@ -477,8 +480,8 @@ export function analyzeSessions(sessionRecords) {
|
|
|
477
480
|
location: { sessionId, recordIndex, timestamp: record.timestamp || null },
|
|
478
481
|
});
|
|
479
482
|
}
|
|
480
|
-
} else if (record.type === "assistant" && hasCurrentPrompt &&
|
|
481
|
-
currentResponseWords += wordCount(
|
|
483
|
+
} else if (record.type === "assistant" && hasCurrentPrompt && assistantResponseProse) {
|
|
484
|
+
currentResponseWords += wordCount(assistantResponseProse);
|
|
482
485
|
}
|
|
483
486
|
if (record.type === "assistant" && text) {
|
|
484
487
|
assistantProse.push(text);
|
|
@@ -554,6 +557,7 @@ export function analyzeSessions(sessionRecords) {
|
|
|
554
557
|
agentUserWordRatio: userInputWords ? Number((agentResponseWords / userInputWords).toFixed(2)) : null,
|
|
555
558
|
averageAgentResponseWords: agentResponseCount ? Math.round(agentResponseWords / agentResponseCount) : 0,
|
|
556
559
|
averageUserInputWords: userInputCount ? Math.round(userInputWords / userInputCount) : 0,
|
|
560
|
+
wordCountMethod: "Counts conversational prose only, excluding injected workspace instructions, code, URLs, paths, and markup.",
|
|
557
561
|
longestSessionTurns: Math.max(0, ...sessionTurnCounts),
|
|
558
562
|
sessionTurnCounts: [...sessionTurnCounts].sort((left, right) => left - right),
|
|
559
563
|
sessionTurnMethod: "Counts each visible, non-meta user message as one turn.",
|
package/server/privacy.mjs
CHANGED
|
@@ -74,7 +74,7 @@ function isSshIdentity(match, offset, source) {
|
|
|
74
74
|
return localPart === "git" || /ssh:\/\/$/i.test(before) || after === ":";
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
const NON_PERSON_WORDS = new Set("agent assistant user system model tool team claude zulip github person someone anyone everyone nobody only the this that new latest direct explicit online private prior session status handoff instruction instructions message messages ping pings task work context state directory window".split(" "));
|
|
77
|
+
const NON_PERSON_WORDS = new Set("agent assistant user system model tool team claude zulip github person someone anyone everyone nobody being only the this that new latest direct explicit online private prior session status handoff instruction instructions message messages ping pings task work context state directory window".split(" "));
|
|
78
78
|
|
|
79
79
|
function replaceLikelyPersonNames(value) {
|
|
80
80
|
return String(value)
|
|
@@ -108,7 +108,7 @@ export function redactText(input, manualTerms = [], { disabledKinds = [], disabl
|
|
|
108
108
|
if (!labeledCredentialValue(match)) return match;
|
|
109
109
|
const replacement = "[REDACTED CREDENTIAL]";
|
|
110
110
|
const enabled = isEnabled("credential", match);
|
|
111
|
-
detections.push(detectionDetails({ kind: "credential", label: "
|
|
111
|
+
detections.push(detectionDetails({ kind: "credential", label: "API keys and secrets", value: match, replacement, offset, source, enabled }));
|
|
112
112
|
return enabled ? replacement : protect(match);
|
|
113
113
|
});
|
|
114
114
|
for (const [pattern, replacement] of [...SECRET_PATTERNS, ...(includeHeuristicSecrets ? HEURISTIC_SECRET_PATTERNS : []), ...PII_PATTERNS]) {
|
|
@@ -117,7 +117,9 @@ export function redactText(input, manualTerms = [], { disabledKinds = [], disabl
|
|
|
117
117
|
const enabled = isEnabled(kind, match);
|
|
118
118
|
detections.push(detectionDetails({
|
|
119
119
|
kind,
|
|
120
|
-
label:
|
|
120
|
+
label: /(?:SECRET|KEY|TOKEN|CREDENTIAL|HIGH-ENTROPY)/.test(replacement)
|
|
121
|
+
? "API keys and secrets"
|
|
122
|
+
: replacement.slice(1, -1).toLowerCase().replace(/\b\w/g, (letter) => letter.toUpperCase()),
|
|
121
123
|
value: match,
|
|
122
124
|
replacement,
|
|
123
125
|
offset,
|
|
@@ -38,7 +38,7 @@ function safeStockPhrases(value) {
|
|
|
38
38
|
|
|
39
39
|
function safeRepeatedInstructions(value) {
|
|
40
40
|
if (!Array.isArray(value)) return [];
|
|
41
|
-
return value.slice(0,
|
|
41
|
+
return value.slice(0, 2).flatMap((item) => {
|
|
42
42
|
const instruction = safeText(item?.instruction, 160).trim();
|
|
43
43
|
const occurrences = Math.round(safeNumber(item?.occurrences, 1_000_000));
|
|
44
44
|
const distinctSessions = Math.round(safeNumber(item?.distinctSessions, 1_000_000));
|