@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 CHANGED
@@ -1 +1 @@
1
- 3ffba7fcfab27ba33e21879f9c097dc9eaafaf4a688857098edbd721e2f81300
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, numbers and labels, never free-form
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, numbers and labels, never free-form
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 = label(input.description);
181
+ detail = labelOf(input.description);
140
182
  break;
141
183
  case "Skill":
142
- detail = label(input.skill);
184
+ detail = labelOf(input.skill);
143
185
  break;
144
186
  }
145
- const text = (detail === null ? name : `${name}: ${detail}`).replace(/\s+/g, " ");
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
- if (existing.description === null && info.description !== null) {
234
- existing.description = info.description;
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.26",
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",