@tangle-network/traces 0.8.16 → 0.8.17
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/{chunk-EOQY2RIK.js → chunk-QM4QAG3V.js} +132 -10
- package/dist/cli.js +31 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -2726,7 +2726,9 @@ function hashId(value, chars = 16) {
|
|
|
2726
2726
|
}
|
|
2727
2727
|
function isoTime(value) {
|
|
2728
2728
|
if (typeof value === "string") {
|
|
2729
|
-
const
|
|
2729
|
+
const trimmed = value.trim();
|
|
2730
|
+
const normalized = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(trimmed) ? `${trimmed.replace(" ", "T")}Z` : trimmed;
|
|
2731
|
+
const ms = Date.parse(normalized);
|
|
2730
2732
|
return Number.isFinite(ms) ? new Date(ms).toISOString() : void 0;
|
|
2731
2733
|
}
|
|
2732
2734
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -2768,14 +2770,19 @@ function parseJsonRows(text) {
|
|
|
2768
2770
|
const trimmed = text.trim();
|
|
2769
2771
|
if (!trimmed) throw new Error("input file is empty");
|
|
2770
2772
|
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2773
|
+
try {
|
|
2774
|
+
const parsed = JSON.parse(trimmed);
|
|
2775
|
+
if (Array.isArray(parsed)) return { rows: parsed };
|
|
2776
|
+
if (isObject(parsed)) {
|
|
2777
|
+
const events = parsed.events;
|
|
2778
|
+
if (Array.isArray(events)) return { rows: events, wrapper: parsed };
|
|
2779
|
+
return { rows: [parsed] };
|
|
2780
|
+
}
|
|
2781
|
+
throw new Error("input JSON must be an object, array, or JSONL rows");
|
|
2782
|
+
} catch (error) {
|
|
2783
|
+
const hasMultipleRows = /\r?\n/.test(trimmed);
|
|
2784
|
+
if (!hasMultipleRows) throw error;
|
|
2777
2785
|
}
|
|
2778
|
-
throw new Error("input JSON must be an object, array, or JSONL rows");
|
|
2779
2786
|
}
|
|
2780
2787
|
return {
|
|
2781
2788
|
rows: trimmed.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line))
|
|
@@ -2788,6 +2795,10 @@ function isOpenInferenceRow(row) {
|
|
|
2788
2795
|
if (!isObject(row)) return false;
|
|
2789
2796
|
return typeof row.trace_id === "string" && typeof row.span_id === "string" && typeof row.name === "string" && typeof row.start_time === "string" && typeof row.end_time === "string" && isObject(row.status) && isObject(row.attributes) && isObject(row.resource) && isObject(row.scope);
|
|
2790
2797
|
}
|
|
2798
|
+
function isIntelligenceSpanRow(row) {
|
|
2799
|
+
if (!isObject(row)) return false;
|
|
2800
|
+
return typeof row.trace_id === "string" && typeof row.name === "string" && isObject(row.attributes) && (row.start_unix_nano != null || row.start_time != null || row.received_at != null);
|
|
2801
|
+
}
|
|
2791
2802
|
function eventType(row) {
|
|
2792
2803
|
return stringValue(row.type) ?? stringValue(row.event) ?? stringValue(row.name) ?? stringValue(row.kind);
|
|
2793
2804
|
}
|
|
@@ -2801,8 +2812,9 @@ function detectFormat(rows, requested) {
|
|
|
2801
2812
|
if (rows.length === 0) throw new Error("input contains no rows");
|
|
2802
2813
|
if (rows.every(isPolicyEvidenceRow)) return "policy-evidence";
|
|
2803
2814
|
if (rows.every(isOpenInferenceRow)) return "openinference";
|
|
2815
|
+
if (rows.every(isIntelligenceSpanRow)) return "intelligence-spans";
|
|
2804
2816
|
if (rows.some(looksLikeSandboxEvent)) return "sandbox-events";
|
|
2805
|
-
throw new Error("could not detect input format; use --format policy-evidence, sandbox-events, or
|
|
2817
|
+
throw new Error("could not detect input format; use --format policy-evidence, sandbox-events, openinference, or intelligence-spans");
|
|
2806
2818
|
}
|
|
2807
2819
|
function requirePolicyEvidenceRows(rows) {
|
|
2808
2820
|
if (!rows.every(isPolicyEvidenceRow)) throw new Error("policy-evidence input must contain only traces.policy_evidence.session rows");
|
|
@@ -2812,6 +2824,10 @@ function requireOpenInferenceRows(rows) {
|
|
|
2812
2824
|
if (!rows.every(isOpenInferenceRow)) throw new Error("openinference input must contain only complete OpenInference span rows");
|
|
2813
2825
|
return rows;
|
|
2814
2826
|
}
|
|
2827
|
+
function requireIntelligenceSpanRows(rows) {
|
|
2828
|
+
if (!rows.every(isIntelligenceSpanRow)) throw new Error("intelligence-spans input must contain only Intelligence span rows");
|
|
2829
|
+
return rows;
|
|
2830
|
+
}
|
|
2815
2831
|
function requireObjectRows(rows) {
|
|
2816
2832
|
if (!rows.every(isObject)) throw new Error("sandbox-events input must contain only JSON object events");
|
|
2817
2833
|
return rows;
|
|
@@ -3008,6 +3024,112 @@ function sandboxEventsToSpans(rows, wrapper) {
|
|
|
3008
3024
|
function statusCode(value) {
|
|
3009
3025
|
return value === "OK" || value === "ERROR" || value === "UNSET" ? value : "UNSET";
|
|
3010
3026
|
}
|
|
3027
|
+
function numberValue(value) {
|
|
3028
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
3029
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
3030
|
+
const parsed = Number(value);
|
|
3031
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
3032
|
+
}
|
|
3033
|
+
return void 0;
|
|
3034
|
+
}
|
|
3035
|
+
function unixNanoTime(value) {
|
|
3036
|
+
if (value == null) return void 0;
|
|
3037
|
+
try {
|
|
3038
|
+
const ns = typeof value === "bigint" ? value : BigInt(String(value));
|
|
3039
|
+
return new Date(Number(ns / 1000000n)).toISOString();
|
|
3040
|
+
} catch {
|
|
3041
|
+
return void 0;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
function firstNumber(row, attrs, keys) {
|
|
3045
|
+
for (const key of keys) {
|
|
3046
|
+
const value = numberValue(row[key]) ?? numberValue(attrs[key]);
|
|
3047
|
+
if (value != null) return value;
|
|
3048
|
+
}
|
|
3049
|
+
return void 0;
|
|
3050
|
+
}
|
|
3051
|
+
var COMPUTED_SPAN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
3052
|
+
"openinference.span.kind",
|
|
3053
|
+
"service.name",
|
|
3054
|
+
"agent.name",
|
|
3055
|
+
"llm.model_name",
|
|
3056
|
+
"tool.name",
|
|
3057
|
+
"llm.input_tokens",
|
|
3058
|
+
"llm.output_tokens",
|
|
3059
|
+
"step",
|
|
3060
|
+
"content"
|
|
3061
|
+
]);
|
|
3062
|
+
function preserveRawAttributes(attrs) {
|
|
3063
|
+
const preserved = {};
|
|
3064
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
3065
|
+
if (COMPUTED_SPAN_ATTRIBUTES.has(key)) {
|
|
3066
|
+
preserved[`traces.raw_attribute.${key}`] = value;
|
|
3067
|
+
} else {
|
|
3068
|
+
preserved[key] = value;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
return preserved;
|
|
3072
|
+
}
|
|
3073
|
+
function intelligenceSpanKind(name, attrs) {
|
|
3074
|
+
const lowered = name.toLowerCase();
|
|
3075
|
+
const spanType = stringValue(attrs["span.type"])?.toLowerCase();
|
|
3076
|
+
if (spanType === "llm_request" || lowered.includes("llm")) return "LLM";
|
|
3077
|
+
if (spanType === "tool" || lowered.includes("tool") || attrs["tool.name"] != null || attrs.tool_name != null || attrs["mcp.tool.name"] != null || attrs["tool.input"] != null || attrs["tool.output"] != null) {
|
|
3078
|
+
return "TOOL";
|
|
3079
|
+
}
|
|
3080
|
+
if (spanType === "interaction" || lowered.includes("interaction") || lowered.includes("agent")) return "AGENT";
|
|
3081
|
+
return "CHAIN";
|
|
3082
|
+
}
|
|
3083
|
+
function intelligenceSpansToSpans(rows) {
|
|
3084
|
+
return rows.map((row, index) => {
|
|
3085
|
+
const attrs = { ...objectValue(row.attributes) ?? {} };
|
|
3086
|
+
const name = stringValue(row.name) ?? "intelligence.span";
|
|
3087
|
+
const startTime = unixNanoTime(row.start_unix_nano) ?? isoTime(row.start_time) ?? stringValue(row.start_time) ?? isoTime(row.received_at) ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
3088
|
+
const endTime = unixNanoTime(row.end_unix_nano) ?? isoTime(row.end_time) ?? stringValue(row.end_time) ?? startTime;
|
|
3089
|
+
const model = stringValue(row.model) ?? stringValue(attrs["llm.model_name"]) ?? stringValue(attrs.model) ?? stringValue(attrs["gen_ai.request.model"]);
|
|
3090
|
+
const tool = stringValue(attrs["tool.name"]) ?? stringValue(attrs.tool_name) ?? stringValue(attrs["mcp.tool.name"]);
|
|
3091
|
+
const sessionId = stringValue(row.session_id) ?? stringValue(attrs["session.id"]);
|
|
3092
|
+
const extra = {
|
|
3093
|
+
...preserveRawAttributes(attrs),
|
|
3094
|
+
"traces.source_format": "intelligence-spans",
|
|
3095
|
+
"traces.row.index": index
|
|
3096
|
+
};
|
|
3097
|
+
copyDefined(extra, {
|
|
3098
|
+
"traces.row.id": stringValue(row.id),
|
|
3099
|
+
[ATTR.SESSION_ID]: sessionId,
|
|
3100
|
+
"session.id": sessionId,
|
|
3101
|
+
"run.id": stringValue(row.run_id),
|
|
3102
|
+
"thread.id": stringValue(row.thread_id),
|
|
3103
|
+
"scenario.id": stringValue(row.scenario_id),
|
|
3104
|
+
"generation.id": stringValue(row.generation),
|
|
3105
|
+
"cell.id": stringValue(row.cell_id),
|
|
3106
|
+
"project.key": stringValue(row.project_key),
|
|
3107
|
+
"llm.cost_usd": numberValue(row.cost_usd),
|
|
3108
|
+
"redaction.version": stringValue(row.redaction_version),
|
|
3109
|
+
"traces.received_at": isoTime(row.received_at)
|
|
3110
|
+
});
|
|
3111
|
+
return span({
|
|
3112
|
+
traceId: stringValue(row.trace_id) ?? hashId(row, 32),
|
|
3113
|
+
spanId: stringValue(row.id) ?? hashId({ row, field: "span" }),
|
|
3114
|
+
parentSpanId: stringValue(row.parent_span_id) ?? null,
|
|
3115
|
+
name,
|
|
3116
|
+
kind: intelligenceSpanKind(name, attrs),
|
|
3117
|
+
startTime,
|
|
3118
|
+
endTime,
|
|
3119
|
+
status: statusCode(row.status_code),
|
|
3120
|
+
statusMessage: stringValue(row.status_message),
|
|
3121
|
+
service: stringValue(attrs["service.name"]) ?? stringValue(attrs.service_name) ?? "tangle-intelligence",
|
|
3122
|
+
agent: stringValue(attrs["agent.name"]) ?? stringValue(attrs["service.name"]) ?? null,
|
|
3123
|
+
model,
|
|
3124
|
+
tool,
|
|
3125
|
+
inputTokens: firstNumber(row, attrs, ["input_tokens", "llm.input_tokens"]),
|
|
3126
|
+
outputTokens: firstNumber(row, attrs, ["output_tokens", "llm.output_tokens"]),
|
|
3127
|
+
step: numberValue(attrs["interaction.sequence"]) ?? index,
|
|
3128
|
+
content: stringValue(attrs.user_prompt) ?? stringValue(attrs["gen_ai.prompt.messages"]) ?? stringValue(attrs["log.content.task"]) ?? null,
|
|
3129
|
+
extra
|
|
3130
|
+
});
|
|
3131
|
+
});
|
|
3132
|
+
}
|
|
3011
3133
|
function openInferenceToSpans(rows) {
|
|
3012
3134
|
return rows.map((row) => {
|
|
3013
3135
|
const attrs = isObject(row.attributes) ? { ...row.attributes } : {};
|
|
@@ -3053,7 +3175,7 @@ function withExportAttributes(spans, attributes) {
|
|
|
3053
3175
|
}
|
|
3054
3176
|
function exportTraceEvidenceRows(rows, opts = {}, wrapper) {
|
|
3055
3177
|
const format = detectFormat(rows, opts.format ?? "auto");
|
|
3056
|
-
const converted = format === "policy-evidence" ? policyEvidenceToSpans(requirePolicyEvidenceRows(rows)) : format === "openinference" ? openInferenceToSpans(requireOpenInferenceRows(rows)) : sandboxEventsToSpans(requireObjectRows(rows), wrapper);
|
|
3178
|
+
const converted = format === "policy-evidence" ? policyEvidenceToSpans(requirePolicyEvidenceRows(rows)) : format === "openinference" ? openInferenceToSpans(requireOpenInferenceRows(rows)) : format === "intelligence-spans" ? intelligenceSpansToSpans(requireIntelligenceSpanRows(rows)) : sandboxEventsToSpans(requireObjectRows(rows), wrapper);
|
|
3057
3179
|
const spans = withExportAttributes(converted, opts.attributes);
|
|
3058
3180
|
if (spans.length === 0) throw new Error(`no spans exported from ${format} input`);
|
|
3059
3181
|
const redacted = redactSpans(spans);
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
writePolicyEvidenceFile,
|
|
41
41
|
writeSessionIndexFile,
|
|
42
42
|
writeTraceEvidenceExportFile
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-QM4QAG3V.js";
|
|
44
44
|
|
|
45
45
|
// src/cli.ts
|
|
46
46
|
import { readFileSync } from "fs";
|
|
@@ -326,10 +326,7 @@ async function cmdInspect(args) {
|
|
|
326
326
|
}
|
|
327
327
|
async function cmdExport(args) {
|
|
328
328
|
if (!args.input) throw new Error("export needs an input file; run `traces export --help` for examples");
|
|
329
|
-
const format = args.format
|
|
330
|
-
if (format !== "auto" && format !== "policy-evidence" && format !== "sandbox-events" && format !== "openinference") {
|
|
331
|
-
throw new Error("--format must be auto, policy-evidence, sandbox-events, or openinference");
|
|
332
|
-
}
|
|
329
|
+
const format = traceEvidenceFormat(args.format);
|
|
333
330
|
const attributes = await loadExportAttributes(args);
|
|
334
331
|
const outPath = args.out ?? args.otlp;
|
|
335
332
|
if (outPath) {
|
|
@@ -342,6 +339,13 @@ async function cmdExport(args) {
|
|
|
342
339
|
const result = await exportTraceEvidenceFile(args.input, { format, attributes });
|
|
343
340
|
process.stdout.write(serializeSpans(result.spans));
|
|
344
341
|
}
|
|
342
|
+
function traceEvidenceFormat(raw) {
|
|
343
|
+
const format = raw ?? "auto";
|
|
344
|
+
if (format !== "auto" && format !== "policy-evidence" && format !== "sandbox-events" && format !== "openinference" && format !== "intelligence-spans") {
|
|
345
|
+
throw new Error("--format must be auto, policy-evidence, sandbox-events, openinference, or intelligence-spans");
|
|
346
|
+
}
|
|
347
|
+
return format;
|
|
348
|
+
}
|
|
345
349
|
function parseAttributeValue(raw) {
|
|
346
350
|
if (raw === "true") return true;
|
|
347
351
|
if (raw === "false") return false;
|
|
@@ -368,7 +372,7 @@ async function loadExportAttributes(args) {
|
|
|
368
372
|
return attributes;
|
|
369
373
|
}
|
|
370
374
|
async function cmdAnalyze(args) {
|
|
371
|
-
const { spans, harness, sessionCount, cwds } = await collectSpans(args);
|
|
375
|
+
const { spans, harness, sessionCount, cwds } = args.input ? await collectImportedSpans(args) : await collectSpans(args);
|
|
372
376
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
373
377
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
374
378
|
const { otlpPath, result } = await analyzeSpans(spans, {
|
|
@@ -405,6 +409,21 @@ ${r.ok ? r.output || "(no output)" : `failed: ${r.error}`}`;
|
|
|
405
409
|
console.log(report);
|
|
406
410
|
}
|
|
407
411
|
}
|
|
412
|
+
async function collectImportedSpans(args) {
|
|
413
|
+
if (!args.input) throw new Error("analyze input missing");
|
|
414
|
+
const attributes = await loadExportAttributes(args);
|
|
415
|
+
const result = await exportTraceEvidenceFile(args.input, {
|
|
416
|
+
format: traceEvidenceFormat(args.format),
|
|
417
|
+
attributes
|
|
418
|
+
});
|
|
419
|
+
const traceIds = new Set(result.spans.map((s) => s.trace_id).filter(Boolean));
|
|
420
|
+
return {
|
|
421
|
+
spans: result.spans,
|
|
422
|
+
harness: result.format,
|
|
423
|
+
sessionCount: traceIds.size || 1,
|
|
424
|
+
cwds: []
|
|
425
|
+
};
|
|
426
|
+
}
|
|
408
427
|
function externalAnalyzersFromArgs(args) {
|
|
409
428
|
return args.analyzers.map(
|
|
410
429
|
(spec) => spec === "halo" ? haloAnalyzer({ defaultPrompt: args.analyzerPrompt, model: args.model }) : commandAnalyzer({ name: spec, command: spec, args: (p, prompt) => prompt ? [p, prompt] : [p] })
|
|
@@ -560,12 +579,14 @@ Input formats:
|
|
|
560
579
|
policy-evidence Compact JSONL rows from \`traces evidence --out policy-evidence.jsonl\`
|
|
561
580
|
sandbox-events Sandbox/OpenCode event arrays with start/raw/result/done/error events
|
|
562
581
|
openinference Existing OpenInference JSONL; rewrites through traces redaction
|
|
582
|
+
intelligence-spans JSONL rows exported from Tangle Intelligence trace spans
|
|
563
583
|
auto Detect the format from the file contents (default)
|
|
564
584
|
|
|
565
585
|
Examples:
|
|
566
586
|
traces evidence --all --since 24h --out policy-evidence.jsonl
|
|
567
587
|
traces export policy-evidence.jsonl --out spans.openinference.jsonl
|
|
568
588
|
traces export sandbox-events.json --format sandbox-events --out spans.openinference.jsonl
|
|
589
|
+
traces export intelligence-spans.jsonl --out spans.openinference.jsonl
|
|
569
590
|
traces export sandbox-events.json --attr task.id=aec-001 --attr outcome.score=1 --out spans.openinference.jsonl
|
|
570
591
|
halo spans.openinference.jsonl --prompt "Analyze this trace slice" --max-turns 1
|
|
571
592
|
|
|
@@ -578,7 +599,7 @@ function usage() {
|
|
|
578
599
|
|
|
579
600
|
Commands:
|
|
580
601
|
list List discovered sessions
|
|
581
|
-
analyze Run analyst suite + loop/waste pipelines
|
|
602
|
+
analyze Run analyst suite + loop/waste pipelines over sessions or an input file
|
|
582
603
|
investigate Run typed investigation flow, including BYO config + recommendations
|
|
583
604
|
improve Write a full improvement artifact directory for review/apply/rerun
|
|
584
605
|
convert Emit OTLP-JSONL only (HALO: use analyze --analyzer halo)
|
|
@@ -599,9 +620,9 @@ Options:
|
|
|
599
620
|
--out <path> Write report to a file
|
|
600
621
|
--dir <path> improve: write artifacts to this directory
|
|
601
622
|
--otlp <path> OTLP artifact path (also evidence provenance / dry-run upload preview)
|
|
602
|
-
--format <kind> export: auto | policy-evidence | sandbox-events | openinference
|
|
603
|
-
--metadata <json> export: attach JSON object fields as span attributes
|
|
604
|
-
--attr <k=v> export: attach one span attribute (repeatable)
|
|
623
|
+
--format <kind> analyze/export file: auto | policy-evidence | sandbox-events | openinference | intelligence-spans
|
|
624
|
+
--metadata <json> analyze/export file: attach JSON object fields as span attributes
|
|
625
|
+
--attr <k=v> analyze/export file: attach one span attribute (repeatable)
|
|
605
626
|
--llm Enable agentic RLM analysts (needs OPENAI_API_KEY / OPENAI_BASE_URL)
|
|
606
627
|
--model <id> --llm model id (e.g. a router model like glm-5.2); default is agent-eval's
|
|
607
628
|
--config <path> investigate/improve: JS config with analysts, external analyzers, or proposal adapter
|
package/dist/index.d.ts
CHANGED
|
@@ -998,7 +998,7 @@ declare function readSessionIndexFile(path: string): Promise<TraceSessionIndex>;
|
|
|
998
998
|
declare function writeInspectionReportFile(report: TraceInspectionReport, outPath?: string): Promise<string>;
|
|
999
999
|
|
|
1000
1000
|
type JsonObject = Record<string, unknown>;
|
|
1001
|
-
type TraceEvidenceInputFormat = 'policy-evidence' | 'sandbox-events' | 'openinference';
|
|
1001
|
+
type TraceEvidenceInputFormat = 'policy-evidence' | 'sandbox-events' | 'openinference' | 'intelligence-spans';
|
|
1002
1002
|
type TraceEvidenceFormatOption = TraceEvidenceInputFormat | 'auto';
|
|
1003
1003
|
interface TraceEvidenceExportOptions {
|
|
1004
1004
|
readonly format?: TraceEvidenceFormatOption;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/traces",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.17",
|
|
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",
|