@thehammer/danx-session-telemetry 0.1.26 → 0.1.27
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/.source-hash +1 -1
- package/dist/activity-observer.d.ts +13 -3
- package/dist/activity-observer.js +54 -11
- package/package.json +1 -1
package/dist/.source-hash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
46e2f294fe40d0bc967337a16d25bee3eeee3a6d9dae5af7ee213a4d519175f3
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*
|
|
34
34
|
* | Field | Read from |
|
|
35
35
|
* |---|---|
|
|
36
|
-
* | `agentType`, `description`, `parentActivityId` | the sub-agent's `meta.json` sidecar (via `SourceDiscoveredInfo`) |
|
|
36
|
+
* | `agentType`, `description`, `parentActivityId` | the sub-agent's `meta.json` sidecar (via `SourceDiscoveredInfo`); the description through {@link redactLabel} |
|
|
37
37
|
* | `lastActivityAt` | the timestamp of its latest `assistant` / `user` line |
|
|
38
38
|
* | `effort` | its latest `assistant` line's top-level `effort` |
|
|
39
39
|
* | `currentActivity` | its latest `tool_use` block, through {@link describeToolCall} |
|
|
@@ -111,11 +111,21 @@ export declare function parseTaskNotification(raw: Record<string, unknown>): {
|
|
|
111
111
|
taskId: string;
|
|
112
112
|
endStatus: ActivityEndStatus;
|
|
113
113
|
} | null;
|
|
114
|
+
/**
|
|
115
|
+
* DX-4498 — an agent-written label as the stats wire may carry it: on one line, with every path-shaped token (any token
|
|
116
|
+
* holding a `/` or `\`) read as `[path]`, and every secret-shaped token (an `ENV_NAME=value` value, or an opaque run of
|
|
117
|
+
* 20+ letters and digits such as a key, token or full SHA) read as `[redacted]`. A label is free text the agent wrote
|
|
118
|
+
* for a person to read (real shell descriptions name absolute paths), and it reaches every reader of the team, so this
|
|
119
|
+
* is the one way any label leaves a transcript. Empty for a blank label.
|
|
120
|
+
*/
|
|
121
|
+
export declare function redactLabel(label: string): string;
|
|
122
|
+
/** DX-4498 — a transcript value read as a label: {@link redactLabel}'s text, or null for a non-string or blank one. */
|
|
123
|
+
export declare function labelOf(value: unknown): string | null;
|
|
114
124
|
/**
|
|
115
125
|
* DX-4498 — a sub-agent's current activity, from one `tool_use` block: the tool's name, plus a label only where the
|
|
116
126
|
* agent wrote one AS a label (a shell call's `description`, an `Agent` call's `description`, a `Skill` call's skill
|
|
117
|
-
* name). Never a command, a file path or a prompt: the stats wire carries ids,
|
|
118
|
-
* transcript content.
|
|
127
|
+
* name), through {@link labelOf}. Never a command, a file path, a secret or a prompt: the stats wire carries ids,
|
|
128
|
+
* numbers and redacted labels, never free-form transcript content.
|
|
119
129
|
*/
|
|
120
130
|
export declare function describeToolCall(name: string, input: Record<string, unknown>): string;
|
|
121
131
|
/**
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*
|
|
34
34
|
* | Field | Read from |
|
|
35
35
|
* |---|---|
|
|
36
|
-
* | `agentType`, `description`, `parentActivityId` | the sub-agent's `meta.json` sidecar (via `SourceDiscoveredInfo`) |
|
|
36
|
+
* | `agentType`, `description`, `parentActivityId` | the sub-agent's `meta.json` sidecar (via `SourceDiscoveredInfo`); the description through {@link redactLabel} |
|
|
37
37
|
* | `lastActivityAt` | the timestamp of its latest `assistant` / `user` line |
|
|
38
38
|
* | `effort` | its latest `assistant` line's top-level `effort` |
|
|
39
39
|
* | `currentActivity` | its latest `tool_use` block, through {@link describeToolCall} |
|
|
@@ -122,27 +122,69 @@ export function parseTaskNotification(raw) {
|
|
|
122
122
|
const endStatus = NOTIFICATION_END_STATUS[match[2].trim()];
|
|
123
123
|
return endStatus === undefined ? null : { taskId: match[1].trim(), endStatus };
|
|
124
124
|
}
|
|
125
|
+
/** DX-4498 — what {@link redactLabel} puts in place of a path-shaped token. */
|
|
126
|
+
const REDACTED_PATH = "[path]";
|
|
127
|
+
/** DX-4498 — what {@link redactLabel} puts in place of a secret-shaped token, or an assignment's value. */
|
|
128
|
+
const REDACTED_SECRET = "[redacted]";
|
|
129
|
+
/** An `ENV_NAME=value` token: its value may be a credential. */
|
|
130
|
+
const ASSIGNMENT_TOKEN = /^([A-Za-z_][A-Za-z0-9_]*)=.+$/;
|
|
131
|
+
/** The characters a key, token, hash or id is written in. */
|
|
132
|
+
const OPAQUE_TOKEN = /^[A-Za-z0-9_+.-]+$/;
|
|
133
|
+
/** The shortest opaque run of letters and digits treated as a key, token, hash or id (a card id or short SHA is far shorter). */
|
|
134
|
+
const OPAQUE_TOKEN_MIN_CHARS = 20;
|
|
135
|
+
/** Punctuation a token may be wrapped in, which never stops it from being read as opaque. */
|
|
136
|
+
const WRAPPING_PUNCTUATION = /^[("'`[]+|[)"'`\],.:;!?]+$/g;
|
|
137
|
+
function redactToken(token) {
|
|
138
|
+
if (/[\\/]/.test(token))
|
|
139
|
+
return REDACTED_PATH;
|
|
140
|
+
const assignment = ASSIGNMENT_TOKEN.exec(token);
|
|
141
|
+
if (assignment !== null)
|
|
142
|
+
return `${assignment[1]}=${REDACTED_SECRET}`;
|
|
143
|
+
const bare = token.replace(WRAPPING_PUNCTUATION, "");
|
|
144
|
+
const opaque = bare.length >= OPAQUE_TOKEN_MIN_CHARS && OPAQUE_TOKEN.test(bare) && /[0-9]/.test(bare) && /[A-Za-z]/.test(bare);
|
|
145
|
+
return opaque ? REDACTED_SECRET : token;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* DX-4498 — an agent-written label as the stats wire may carry it: on one line, with every path-shaped token (any token
|
|
149
|
+
* holding a `/` or `\`) read as `[path]`, and every secret-shaped token (an `ENV_NAME=value` value, or an opaque run of
|
|
150
|
+
* 20+ letters and digits such as a key, token or full SHA) read as `[redacted]`. A label is free text the agent wrote
|
|
151
|
+
* for a person to read (real shell descriptions name absolute paths), and it reaches every reader of the team, so this
|
|
152
|
+
* is the one way any label leaves a transcript. Empty for a blank label.
|
|
153
|
+
*/
|
|
154
|
+
export function redactLabel(label) {
|
|
155
|
+
return label
|
|
156
|
+
.split(/\s+/)
|
|
157
|
+
.filter((token) => token !== "")
|
|
158
|
+
.map(redactToken)
|
|
159
|
+
.join(" ");
|
|
160
|
+
}
|
|
161
|
+
/** DX-4498 — a transcript value read as a label: {@link redactLabel}'s text, or null for a non-string or blank one. */
|
|
162
|
+
export function labelOf(value) {
|
|
163
|
+
if (typeof value !== "string")
|
|
164
|
+
return null;
|
|
165
|
+
const label = redactLabel(value);
|
|
166
|
+
return label === "" ? null : label;
|
|
167
|
+
}
|
|
125
168
|
/**
|
|
126
169
|
* DX-4498 — a sub-agent's current activity, from one `tool_use` block: the tool's name, plus a label only where the
|
|
127
170
|
* agent wrote one AS a label (a shell call's `description`, an `Agent` call's `description`, a `Skill` call's skill
|
|
128
|
-
* name). Never a command, a file path or a prompt: the stats wire carries ids,
|
|
129
|
-
* transcript content.
|
|
171
|
+
* name), through {@link labelOf}. Never a command, a file path, a secret or a prompt: the stats wire carries ids,
|
|
172
|
+
* numbers and redacted labels, never free-form transcript content.
|
|
130
173
|
*/
|
|
131
174
|
export function describeToolCall(name, input) {
|
|
132
|
-
const label = (v) => (typeof v === "string" && v.trim() !== "" ? v.trim() : null);
|
|
133
175
|
let detail = null;
|
|
134
176
|
switch (name) {
|
|
135
177
|
case "Bash":
|
|
136
178
|
case "PowerShell":
|
|
137
179
|
case "Agent":
|
|
138
180
|
case "Task":
|
|
139
|
-
detail =
|
|
181
|
+
detail = labelOf(input.description);
|
|
140
182
|
break;
|
|
141
183
|
case "Skill":
|
|
142
|
-
detail =
|
|
184
|
+
detail = labelOf(input.skill);
|
|
143
185
|
break;
|
|
144
186
|
}
|
|
145
|
-
const text =
|
|
187
|
+
const text = detail === null ? name : `${name}: ${detail}`;
|
|
146
188
|
return text.length > CURRENT_ACTIVITY_MAX_CHARS ? `${text.slice(0, CURRENT_ACTIVITY_MAX_CHARS - 3)}...` : text;
|
|
147
189
|
}
|
|
148
190
|
/**
|
|
@@ -230,8 +272,9 @@ export function createActivityObserver(deps) {
|
|
|
230
272
|
existing.agentType = info.agentType;
|
|
231
273
|
filled = true;
|
|
232
274
|
}
|
|
233
|
-
|
|
234
|
-
|
|
275
|
+
const description = labelOf(info.description);
|
|
276
|
+
if (existing.description === null && description !== null) {
|
|
277
|
+
existing.description = description;
|
|
235
278
|
filled = true;
|
|
236
279
|
}
|
|
237
280
|
if (existing.parentActivityId === null && info.parentSource !== null) {
|
|
@@ -246,8 +289,8 @@ export function createActivityObserver(deps) {
|
|
|
246
289
|
kind: "agent",
|
|
247
290
|
activityId: info.source,
|
|
248
291
|
agentType: info.agentType,
|
|
249
|
-
// DX-4498 — the label its spawning `Agent` call gave it, from the sidecar.
|
|
250
|
-
description: info.description,
|
|
292
|
+
// DX-4498 — the label its spawning `Agent` call gave it, from the sidecar, redacted like every label.
|
|
293
|
+
description: labelOf(info.description),
|
|
251
294
|
startedAt: info.discoveredAt,
|
|
252
295
|
lastActivityAt: info.discoveredAt,
|
|
253
296
|
finishedAt: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thehammer/danx-session-telemetry",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "Shared Claude Code transcript collector (DX-2962) — incremental JSONL tailing for the danxbot worker and the danx-dashboard-mcp desktop bridge, so both run identical collector code.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|