@tangle-network/traces 0.4.1 → 0.5.0

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/README.md CHANGED
@@ -102,19 +102,33 @@ traces upload --since 24h # upload last day to the Inte
102
102
  | `--llm` / `--budget <usd>` | Enable agentic analysts (needs `OPENAI_API_KEY`) / cap their spend |
103
103
  | `--interval <s>` / `--window <m>` | `watch`: poll seconds (default 5) / active-session window minutes (default 30) |
104
104
  | `--min-loop <n>` | Identical repeated calls before flagging a loop (default 3) |
105
+ | `--no-content` | `upload`: send metadata only — strip all prompt/response text |
105
106
  | `--dry-run` / `--yes` | `upload`: preview without sending / skip the confirm prompt |
106
107
 
107
108
  ## Upload to the Intelligence Platform
108
109
 
109
- `upload` **redacts PII/secrets locally before anything leaves the machine**, dedups against already-uploaded sessions, and tags each with metadata (harness, cwd, git branch, host).
110
+ `upload` **redacts locally before anything leaves the machine**, dedups against already-uploaded sessions, and tags each with metadata (harness, cwd, git branch, host).
110
111
 
111
112
  ```bash
112
- traces upload --since 24h --dry-run # see exactly what would be sent — no network
113
- traces upload --since 24h # send it
113
+ traces upload --since 24h --dry-run # see exactly what would be sent — no network
114
+ traces upload --since 24h --no-content # send metadata only — drop all prompt/response text
115
+ traces upload --since 24h # send it
114
116
  ```
115
117
 
116
118
  It needs `TANGLE_INGEST_URL` (or `TANGLE_ORCHESTRATOR_URL`), `TANGLE_INGEST_API_KEY` (or `TANGLE_API_KEY`), and `TANGLE_TENANT_ID`. Without them, `--dry-run` still works fully.
117
119
 
120
+ ### Redaction scope — read this before uploading prose
121
+
122
+ Redaction is **best-effort regex** for *structured* secrets and credentials: API keys, GitHub/cloud tokens, JWTs, bearer headers, private-key blocks, `KEY=secret` assignments, and credentials embedded in URLs. It runs over every span attribute, including the captured prompt/response text.
123
+
124
+ It does **not** catch free-form PII — names, postal addresses, phone numbers in prose — which needs a context-aware model. Three postures, strongest first:
125
+
126
+ 1. **`--no-content`** — upload metadata only (tool calls, tokens, timing, loop signal); no prose leaves the machine.
127
+ 2. Run an ML PII scrubber (e.g. [`openai/privacy-filter`](https://github.com/openai/privacy-filter)) on the platform ingest side as defense-in-depth.
128
+ 3. Default — regex redaction of structured secrets.
129
+
130
+ Always `--dry-run` first to see exactly what would be sent.
131
+
118
132
  ## Library (SDK)
119
133
 
120
134
  The CLI is a thin consumer of these exports.
@@ -139,15 +139,38 @@ async function runPipelines(spans, opts = {}) {
139
139
  return { stuckLoops, toolUse };
140
140
  }
141
141
 
142
+ // src/adapters/conversation.ts
143
+ var CONTENT_CAP = 8e3;
144
+ function capText(raw) {
145
+ const t = raw.trim();
146
+ if (t.length <= CONTENT_CAP) return t;
147
+ return `${t.slice(0, CONTENT_CAP)}\u2026 [+${t.length - CONTENT_CAP} chars]`;
148
+ }
149
+ function userPromptSpan(o) {
150
+ return span({
151
+ traceId: o.traceId,
152
+ spanId: o.spanId,
153
+ parentSpanId: o.parentSpanId,
154
+ name: "user.prompt",
155
+ kind: "CHAIN",
156
+ startTime: o.startTime,
157
+ service: o.service ?? null,
158
+ agent: o.agent ?? null,
159
+ step: o.step,
160
+ content: o.content
161
+ });
162
+ }
163
+
142
164
  // src/adapters/amp.ts
143
165
  import { readdir, readFile, stat } from "fs/promises";
144
166
  import { homedir } from "os";
145
167
  import { basename, join } from "path";
146
168
  var SERVICE = "amp";
147
- var CONTENT_CAP = 8e3;
148
169
  function textOf(content) {
149
170
  if (typeof content === "string") return content.slice(0, CONTENT_CAP);
150
- return (Array.isArray(content) ? content : []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n").trim().slice(0, CONTENT_CAP);
171
+ return capText(
172
+ (Array.isArray(content) ? content : []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n")
173
+ );
151
174
  }
152
175
  var AmpAdapter = class {
153
176
  harness = "amp";
@@ -201,12 +224,10 @@ var AmpAdapter = class {
201
224
  const prompt = textOf(m.content);
202
225
  if (prompt) {
203
226
  spans.push(
204
- span({
227
+ userPromptSpan({
205
228
  traceId,
206
229
  spanId: `${llmId}:user`,
207
230
  parentSpanId: rootId,
208
- name: "user.prompt",
209
- kind: "CHAIN",
210
231
  startTime: start,
211
232
  service: SERVICE,
212
233
  agent: SERVICE,
@@ -290,10 +311,11 @@ function inputTokens(u) {
290
311
  function asBlocks(content) {
291
312
  return Array.isArray(content) ? content : [];
292
313
  }
293
- var CONTENT_CAP2 = 8e3;
294
314
  function textOf2(content) {
295
- if (typeof content === "string") return content.slice(0, CONTENT_CAP2);
296
- return asBlocks(content).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n").trim().slice(0, CONTENT_CAP2);
315
+ if (typeof content === "string") return capText(content);
316
+ return capText(
317
+ asBlocks(content).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n")
318
+ );
297
319
  }
298
320
  function stringifyToolResult(content) {
299
321
  if (typeof content === "string") return content;
@@ -354,12 +376,10 @@ function parseClaudeStream(events, ctx) {
354
376
  const prompt = textOf2(ev.message.content);
355
377
  if (prompt) {
356
378
  spans.push(
357
- span({
379
+ userPromptSpan({
358
380
  traceId: ctx.traceId,
359
381
  spanId: `${ctx.idPrefix}${uid}:user`,
360
382
  parentSpanId: ctx.rootParent,
361
- name: "user.prompt",
362
- kind: "CHAIN",
363
383
  startTime: ts,
364
384
  service: SERVICE2,
365
385
  agent: ctx.agent,
@@ -518,9 +538,8 @@ function contentToString(content) {
518
538
  }
519
539
  return "";
520
540
  }
521
- var CONTENT_CAP3 = 8e3;
522
541
  function textOf3(content) {
523
- return contentToString(content).trim().slice(0, CONTENT_CAP3);
542
+ return capText(contentToString(content));
524
543
  }
525
544
  function outputIsError(output) {
526
545
  const s = typeof output === "string" ? output : JSON.stringify(output ?? "");
@@ -673,17 +692,15 @@ var CodexAdapter = class {
673
692
  const prompt = textOf3(l.payload.content);
674
693
  if (prompt) {
675
694
  spans.push(
676
- span({
695
+ userPromptSpan({
677
696
  traceId,
678
697
  spanId: `msg:${step}:user`,
679
698
  parentSpanId: rootId,
680
- name: "user.prompt",
681
- kind: "CHAIN",
682
699
  startTime: ts,
700
+ content: prompt,
683
701
  service: SERVICE3,
684
702
  agent: SERVICE3,
685
- step,
686
- content: prompt
703
+ step
687
704
  })
688
705
  );
689
706
  step += 1;
@@ -844,9 +861,10 @@ import { readdir as readdir5, readFile as readFile5, stat as stat5 } from "fs/pr
844
861
  import { homedir as homedir5 } from "os";
845
862
  import { basename as basename4, join as join5 } from "path";
846
863
  var SERVICE5 = "factory";
847
- var CONTENT_CAP4 = 8e3;
848
864
  function textOf4(content) {
849
- return (content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("").trim().slice(0, CONTENT_CAP4);
865
+ return capText(
866
+ (content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("")
867
+ );
850
868
  }
851
869
  function parseLines4(raw) {
852
870
  const out = [];
@@ -938,17 +956,15 @@ var FactoryAdapter = class {
938
956
  if (role === "user") {
939
957
  if (text) {
940
958
  spans.push(
941
- span({
959
+ userPromptSpan({
942
960
  traceId,
943
961
  spanId: `user:${l.id ?? step}`,
944
962
  parentSpanId: rootId,
945
- name: "user.prompt",
946
- kind: "CHAIN",
947
963
  startTime: ts,
964
+ content: text,
948
965
  service: SERVICE5,
949
966
  agent: SERVICE5,
950
- step,
951
- content: text
967
+ step
952
968
  })
953
969
  );
954
970
  step += 1;
@@ -1010,9 +1026,8 @@ function tokens(t) {
1010
1026
  if (!t) return null;
1011
1027
  return t.actual ?? t.approx ?? null;
1012
1028
  }
1013
- var CONTENT_CAP5 = 8e3;
1014
1029
  function textOf5(content) {
1015
- return (content ?? "").trim().slice(0, CONTENT_CAP5);
1030
+ return capText(content ?? "");
1016
1031
  }
1017
1032
  var ForgeAdapter = class {
1018
1033
  harness = "forge";
@@ -1070,17 +1085,15 @@ var ForgeAdapter = class {
1070
1085
  const prompt = textOf5(entry.text.content);
1071
1086
  if (prompt) {
1072
1087
  spans.push(
1073
- span({
1088
+ userPromptSpan({
1074
1089
  traceId,
1075
1090
  spanId: `user:${step}`,
1076
1091
  parentSpanId: rootId,
1077
- name: "user.prompt",
1078
- kind: "CHAIN",
1079
1092
  startTime: ts,
1093
+ content: prompt,
1080
1094
  service: SERVICE6,
1081
1095
  agent: SERVICE6,
1082
- step,
1083
- content: prompt
1096
+ step
1084
1097
  })
1085
1098
  );
1086
1099
  step += 1;
@@ -1146,10 +1159,9 @@ function iso(ts, fallback) {
1146
1159
  function toolStatusError(status) {
1147
1160
  return status != null && /error|fail|cancel/i.test(status);
1148
1161
  }
1149
- var CONTENT_CAP6 = 8e3;
1150
1162
  function textOf6(content) {
1151
1163
  const s = typeof content === "string" ? content : JSON.stringify(content ?? "");
1152
- return s.trim().slice(0, CONTENT_CAP6);
1164
+ return capText(s);
1153
1165
  }
1154
1166
  var GeminiFamilyAdapter = class {
1155
1167
  harness;
@@ -1231,17 +1243,15 @@ var GeminiFamilyAdapter = class {
1231
1243
  const prompt = textOf6(m.content);
1232
1244
  if (prompt) {
1233
1245
  spans.push(
1234
- span({
1246
+ userPromptSpan({
1235
1247
  traceId,
1236
1248
  spanId: `${llmId}:user`,
1237
1249
  parentSpanId: rootId,
1238
- name: "user.prompt",
1239
- kind: "CHAIN",
1240
1250
  startTime: ts,
1251
+ content: prompt,
1241
1252
  service: this.service,
1242
1253
  agent: this.service,
1243
- step,
1244
- content: prompt
1254
+ step
1245
1255
  })
1246
1256
  );
1247
1257
  step += 1;
@@ -1304,9 +1314,10 @@ var SERVICE7 = "opencode";
1304
1314
  function isoOf(ms) {
1305
1315
  return new Date(ms ?? 0).toISOString();
1306
1316
  }
1307
- var CONTENT_CAP7 = 8e3;
1308
1317
  function textOf7(parts) {
1309
- return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n").trim().slice(0, CONTENT_CAP7);
1318
+ return capText(
1319
+ parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n")
1320
+ );
1310
1321
  }
1311
1322
  var OpencodeAdapter = class {
1312
1323
  harness = "opencode";
@@ -1407,12 +1418,10 @@ var OpencodeAdapter = class {
1407
1418
  step += 1;
1408
1419
  if (msg.role === "user" && turnText) {
1409
1420
  spans.push(
1410
- span({
1421
+ userPromptSpan({
1411
1422
  traceId,
1412
1423
  spanId: `${llmId}:user`,
1413
1424
  parentSpanId: rootId,
1414
- name: "user.prompt",
1415
- kind: "CHAIN",
1416
1425
  startTime: isoOf(msg.time?.created),
1417
1426
  service: SERVICE7,
1418
1427
  agent: SERVICE7,
@@ -1472,10 +1481,11 @@ function isToolBlock(b) {
1472
1481
  function isToolResultBlock(b) {
1473
1482
  return typeof b.type === "string" && /tool[_-]?result|tool[_-]?output/i.test(b.type);
1474
1483
  }
1475
- var CONTENT_CAP8 = 8e3;
1476
1484
  function textOf8(content) {
1477
1485
  if (!content) return "";
1478
- return content.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("").trim().slice(0, CONTENT_CAP8);
1486
+ return capText(
1487
+ content.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("")
1488
+ );
1479
1489
  }
1480
1490
  var PiAdapter = class {
1481
1491
  harness = "pi";
@@ -1547,12 +1557,10 @@ var PiAdapter = class {
1547
1557
  const prompt = textOf8(msg.content);
1548
1558
  if (prompt) {
1549
1559
  spans.push(
1550
- span({
1560
+ userPromptSpan({
1551
1561
  traceId,
1552
1562
  spanId: `${mid}:user`,
1553
1563
  parentSpanId: rootId,
1554
- name: "user.prompt",
1555
- kind: "CHAIN",
1556
1564
  startTime: ts,
1557
1565
  service: SERVICE8,
1558
1566
  agent: SERVICE8,
@@ -1718,7 +1726,7 @@ var QwenAdapter = class {
1718
1726
  inputTokens: r.usageMetadata?.promptTokenCount ?? null,
1719
1727
  outputTokens: r.usageMetadata?.candidatesTokenCount ?? null,
1720
1728
  step,
1721
- content: textOf9(r.message?.parts).slice(0, 8e3) || null
1729
+ content: capText(textOf9(r.message?.parts)) || null
1722
1730
  })
1723
1731
  );
1724
1732
  lastLlm = llmId;
@@ -1746,15 +1754,13 @@ var QwenAdapter = class {
1746
1754
  step += 1;
1747
1755
  }
1748
1756
  } else if (r.type === "user") {
1749
- const prompt = textOf9(r.message?.parts).slice(0, 8e3);
1757
+ const prompt = capText(textOf9(r.message?.parts));
1750
1758
  if (prompt) {
1751
1759
  spans.push(
1752
- span({
1760
+ userPromptSpan({
1753
1761
  traceId,
1754
1762
  spanId: `user:${r.uuid ?? step}`,
1755
1763
  parentSpanId: rootId,
1756
- name: "user.prompt",
1757
- kind: "CHAIN",
1758
1764
  startTime: ts,
1759
1765
  service: SERVICE9,
1760
1766
  agent: SERVICE9,
@@ -1960,6 +1966,12 @@ var CODING_REDACTION_RULES = [
1960
1966
  {
1961
1967
  id: "assigned-secret",
1962
1968
  pattern: /\b(?:api[_-]?key|secret|token|password|passwd|access[_-]?token|client[_-]?secret)\b\s*[:=]\s*["']?[A-Za-z0-9._\-]{12,}["']?/gi
1969
+ },
1970
+ // Credentials embedded in URLs — common when a prompt pastes a curl/clone line.
1971
+ { id: "url-userinfo", pattern: /\b[a-z][a-z0-9+.-]*:\/\/[^\s/:@]+:[^\s/@]+@/gi },
1972
+ {
1973
+ id: "url-secret-param",
1974
+ pattern: /[?&](?:access[_-]?token|api[_-]?key|apikey|auth|token|secret|password|sig|signature)=[^&\s"'#)]+/gi
1963
1975
  }
1964
1976
  ];
1965
1977
  var TRACES_REDACTION_RULES = [...DEFAULT_REDACTION_RULES, ...CODING_REDACTION_RULES];
@@ -2088,8 +2100,15 @@ function toTraceSpanEvents(spans, rootMeta) {
2088
2100
  };
2089
2101
  });
2090
2102
  }
2103
+ function stripSpanContent(spans) {
2104
+ return spans.map((s) => {
2105
+ if (s.attributes["content"] == null) return s;
2106
+ const { content: _drop, ...attributes } = s.attributes;
2107
+ return { ...s, attributes };
2108
+ });
2109
+ }
2091
2110
  async function executeUpload(plan, opts = {}) {
2092
- const newItems = plan.items.filter((i) => i.isNew);
2111
+ const newItems = plan.items.filter((i) => i.isNew).map((i) => opts.stripContent ? { ...i, spans: stripSpanContent(i.spans) } : i);
2093
2112
  const skipped = plan.items.length - newItems.length;
2094
2113
  const redactionCount = newItems.reduce((n, i) => n + i.redaction.redactionCount, 0);
2095
2114
  const uploadedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -2146,6 +2165,9 @@ export {
2146
2165
  parseSince,
2147
2166
  toRuntimeStore,
2148
2167
  runPipelines,
2168
+ CONTENT_CAP,
2169
+ capText,
2170
+ userPromptSpan,
2149
2171
  AmpAdapter,
2150
2172
  ClaudeAdapter,
2151
2173
  CodexAdapter,
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  selectAdapters,
11
11
  watchSessions,
12
12
  writeOtlpFile
13
- } from "./chunk-33NBMIH4.js";
13
+ } from "./chunk-HF2KGEBW.js";
14
14
 
15
15
  // src/cli.ts
16
16
  import { stat, writeFile } from "fs/promises";
@@ -105,7 +105,8 @@ function parseArgs(argv) {
105
105
  window: 30,
106
106
  minLoop: 3,
107
107
  dryRun: false,
108
- yes: false
108
+ yes: false,
109
+ noContent: false
109
110
  };
110
111
  for (let i = 1; i < argv.length; i++) {
111
112
  const arg = argv[i];
@@ -154,6 +155,9 @@ function parseArgs(argv) {
154
155
  case "--dry-run":
155
156
  a.dryRun = true;
156
157
  break;
158
+ case "--no-content":
159
+ a.noContent = true;
160
+ break;
157
161
  case "--yes":
158
162
  case "-y":
159
163
  a.yes = true;
@@ -322,7 +326,7 @@ Window: since ${new Date(sinceMs).toISOString()}
322
326
  return;
323
327
  }
324
328
  if (args.dryRun) {
325
- const res2 = await executeUpload(plan, { dryRun: true, otlpOut: args.otlp });
329
+ const res2 = await executeUpload(plan, { dryRun: true, otlpOut: args.otlp, stripContent: args.noContent });
326
330
  console.log(`dry run \u2014 ${newItems.length} session(s), ${totalRedactions} redaction(s). Redacted OTLP \u2192 ${res2.otlpPath}`);
327
331
  console.log("No upload performed. Set TANGLE_INGEST_URL / TANGLE_INGEST_API_KEY / TANGLE_TENANT_ID and drop --dry-run to send.");
328
332
  return;
@@ -335,7 +339,7 @@ Window: since ${new Date(sinceMs).toISOString()}
335
339
  }
336
340
  }
337
341
  const res = await executeUpload(plan, { log: (m) => w(`${m}
338
- `) });
342
+ `), stripContent: args.noContent });
339
343
  console.log(
340
344
  `Uploaded ${res.uploadedSessions} session(s), ${res.acceptedSpans} spans accepted, ${res.redactionCount} redaction(s); ${res.skippedSessions} already-uploaded skipped.`
341
345
  );
@@ -365,6 +369,7 @@ Options:
365
369
  --window <m> watch: only sessions active in the last N minutes (default 30)
366
370
  --min-loop <n> Min identical repeated calls to flag a loop (default 3)
367
371
  --dry-run upload: redact + dedup + preview, write OTLP, but do NOT send
372
+ --no-content upload: strip prompt/response text \u2014 send metadata only
368
373
  --yes, -y upload: skip the confirmation prompt
369
374
 
370
375
  Upload env: TANGLE_INGEST_URL (or TANGLE_ORCHESTRATOR_URL), TANGLE_INGEST_API_KEY (or TANGLE_API_KEY), TANGLE_TENANT_ID`);
package/dist/index.d.ts CHANGED
@@ -436,6 +436,37 @@ declare class ForgeAdapter implements HarnessTraceAdapter {
436
436
  parse(ref: SessionRef): Promise<OtlpSpan[]>;
437
437
  }
438
438
 
439
+ /**
440
+ * Shared conversation-capture primitives for the harness adapters.
441
+ *
442
+ * Every adapter records the human's prompt and the assistant's prose, not just
443
+ * the execution skeleton (LLM token spans + tool calls). These helpers keep that
444
+ * capture identical across harnesses: one text cap, one `user.prompt` span shape.
445
+ */
446
+
447
+ /** Max chars of conversation text kept per span — enough for prompt/response
448
+ * analysis, bounded for storage + redaction cost. */
449
+ declare const CONTENT_CAP = 8000;
450
+ /** Trim and cap a message's text at {@link CONTENT_CAP}. When the text is cut, a
451
+ * visible `… [+N chars]` marker is appended so analysts (and readers) know the
452
+ * content was truncated rather than silently lost. */
453
+ declare function capText(raw: string): string;
454
+ interface UserPromptInput {
455
+ traceId: string;
456
+ spanId: string;
457
+ parentSpanId: string | null;
458
+ startTime: string;
459
+ /** The human's prompt text (extracted by the adapter; cap with capText). */
460
+ content: string;
461
+ service?: string | null;
462
+ agent?: string | null;
463
+ step?: number;
464
+ }
465
+ /** The canonical `user.prompt` span. Every adapter emits the human's turn the
466
+ * same way — a CHAIN span, because a user message is not an LLM call — so
467
+ * analysts see one consistent shape regardless of harness. */
468
+ declare function userPromptSpan(o: UserPromptInput): OtlpSpan;
469
+
439
470
  /**
440
471
  * Run agent-eval's shipped trace pipelines over normalized harness spans.
441
472
  *
@@ -589,9 +620,15 @@ declare function watchSessions(opts: ObserverOptions): Promise<void>;
589
620
  * Reuses agent-eval's `redactValue` + `DEFAULT_REDACTION_RULES` (email, IPs,
590
621
  * generic secret keys, …) and layers on coding-session-specific rules that the
591
622
  * defaults miss — GitHub tokens, cloud keys, JWTs, bearer headers, private-key
592
- * blocks, and `key=secret` assignments common in shell/tool output. Redaction
593
- * runs over every span attribute + status message, so what leaves the machine
594
- * is already scrubbed.
623
+ * blocks, `key=secret` assignments, and credentials embedded in URLs. Redaction
624
+ * runs over every span attribute + status message (including captured prompt /
625
+ * response `content`), so what leaves the machine is already scrubbed.
626
+ *
627
+ * Scope, stated honestly: this is **best-effort regex** for *structured* secrets
628
+ * and credentials. It does NOT catch free-form PII — names, postal addresses,
629
+ * phone numbers, account numbers in prose — which need a context-aware model.
630
+ * For that assurance, run an ML PII scrubber (e.g. openai/privacy-filter) on the
631
+ * ingest side of the platform, or upload metadata-only with `--no-content`.
595
632
  */
596
633
 
597
634
  /** Secrets common in coding-agent traces that the substrate defaults don't cover. */
@@ -709,9 +746,13 @@ interface ExecuteOptions {
709
746
  otlpOut?: string;
710
747
  /** Custom sink. Defaults to the hosted Tangle Intelligence client from env. */
711
748
  backend?: UploadBackend;
749
+ /** Metadata-only upload: drop captured prompt/response `content` from every
750
+ * span before it leaves the machine (still keeps tool calls, tokens, timing,
751
+ * loop signal). The strongest privacy posture when prose can't leave. */
752
+ stripContent?: boolean;
712
753
  log?: (msg: string) => void;
713
754
  }
714
755
  /** Send the NEW items (or, on dryRun, write the redacted OTLP that would send). */
715
756
  declare function executeUpload(plan: UploadPlan, opts?: ExecuteOptions): Promise<UploadResult>;
716
757
 
717
- export { ATTR, AmpAdapter, type AnalyzeOptions, type AnalyzeResult, CODING_REDACTION_RULES, ClaudeAdapter, CodexAdapter, type CollectOptions, CopilotAdapter, DEFAULT_HARNESS, type ExecuteOptions, FactoryAdapter, ForgeAdapter, GeminiAdapter, type HarnessTraceAdapter, INGEST_SOURCE_CLI, type LocateOptions, type ObservedLoop, type ObserverOptions, OpencodeAdapter, type OtlpSpan, type OtlpSpanKind, type OtlpStatusCode, PiAdapter, type PipelineOptions, type PipelineReport, type PlanOptions, QwenAdapter, type RuntimeTrace, type ScanOptions, type ScannedSession, type SessionBatch, type SessionRef, type SpanInput, type SpanRedaction, TRACES_REDACTION_RULES, type UploadBackend, type UploadItem, type UploadPlan, type UploadRecord, type UploadResult, type UploadState, alreadyUploaded, analyzeSpans, collectSessions, executeUpload, knownHarnesses, listAdapters, loadState, parseIsoToEpochMs, parseSince, planUpload, redactSpans, resolveAdapter, runPipelines, saveState, scanSessions, selectAdapters, serializeSpans, sessionHash, span, toRuntimeStore, toTraceSpanEvents, uploadKey, watchSessions, writeOtlpFile };
758
+ export { ATTR, AmpAdapter, type AnalyzeOptions, type AnalyzeResult, CODING_REDACTION_RULES, CONTENT_CAP, ClaudeAdapter, CodexAdapter, type CollectOptions, CopilotAdapter, DEFAULT_HARNESS, type ExecuteOptions, FactoryAdapter, ForgeAdapter, GeminiAdapter, type HarnessTraceAdapter, INGEST_SOURCE_CLI, type LocateOptions, type ObservedLoop, type ObserverOptions, OpencodeAdapter, type OtlpSpan, type OtlpSpanKind, type OtlpStatusCode, PiAdapter, type PipelineOptions, type PipelineReport, type PlanOptions, QwenAdapter, type RuntimeTrace, type ScanOptions, type ScannedSession, type SessionBatch, type SessionRef, type SpanInput, type SpanRedaction, TRACES_REDACTION_RULES, type UploadBackend, type UploadItem, type UploadPlan, type UploadRecord, type UploadResult, type UploadState, alreadyUploaded, analyzeSpans, capText, collectSessions, executeUpload, knownHarnesses, listAdapters, loadState, parseIsoToEpochMs, parseSince, planUpload, redactSpans, resolveAdapter, runPipelines, saveState, scanSessions, selectAdapters, serializeSpans, sessionHash, span, toRuntimeStore, toTraceSpanEvents, uploadKey, userPromptSpan, watchSessions, writeOtlpFile };
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  ATTR,
3
3
  AmpAdapter,
4
4
  CODING_REDACTION_RULES,
5
+ CONTENT_CAP,
5
6
  ClaudeAdapter,
6
7
  CodexAdapter,
7
8
  CopilotAdapter,
@@ -16,6 +17,7 @@ import {
16
17
  TRACES_REDACTION_RULES,
17
18
  alreadyUploaded,
18
19
  analyzeSpans,
20
+ capText,
19
21
  executeUpload,
20
22
  knownHarnesses,
21
23
  listAdapters,
@@ -35,9 +37,10 @@ import {
35
37
  toRuntimeStore,
36
38
  toTraceSpanEvents,
37
39
  uploadKey,
40
+ userPromptSpan,
38
41
  watchSessions,
39
42
  writeOtlpFile
40
- } from "./chunk-33NBMIH4.js";
43
+ } from "./chunk-HF2KGEBW.js";
41
44
 
42
45
  // src/collect.ts
43
46
  async function collectSessions(opts = {}) {
@@ -66,6 +69,7 @@ export {
66
69
  AmpAdapter,
67
70
  AnalystRegistry,
68
71
  CODING_REDACTION_RULES,
72
+ CONTENT_CAP,
69
73
  ClaudeAdapter,
70
74
  CodexAdapter,
71
75
  CopilotAdapter,
@@ -82,6 +86,7 @@ export {
82
86
  alreadyUploaded,
83
87
  analyzeSpans,
84
88
  buildDefaultAnalystRegistry,
89
+ capText,
85
90
  collectSessions,
86
91
  createHostedClient,
87
92
  executeUpload,
@@ -107,6 +112,7 @@ export {
107
112
  toRuntimeStore,
108
113
  toTraceSpanEvents,
109
114
  uploadKey,
115
+ userPromptSpan,
110
116
  watchSessions,
111
117
  writeOtlpFile
112
118
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/traces",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Point it at your coding-agent session traces (Claude Code, Codex, OpenCode, Gemini, Pi, …) and get failure-mode + efficiency findings. CLI + SDK over the @tangle-network/agent-eval analyst suite — observe live sessions, run your own analysts, redact, and upload to the Tangle Intelligence Platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",