@warlock.js/ai-panoptic 4.3.0 → 4.4.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/cjs/index.cjs +259 -52
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/collector/collector.d.mts +2 -1
  5. package/esm/collector/collector.d.mts.map +1 -1
  6. package/esm/collector/collector.mjs +10 -4
  7. package/esm/collector/collector.mjs.map +1 -1
  8. package/esm/collector/content-capture.type.d.mts +42 -0
  9. package/esm/collector/content-capture.type.d.mts.map +1 -0
  10. package/esm/collector/index.d.mts +1 -0
  11. package/esm/collector/report-to-span.d.mts +8 -1
  12. package/esm/collector/report-to-span.d.mts.map +1 -1
  13. package/esm/collector/report-to-span.mjs +66 -2
  14. package/esm/collector/report-to-span.mjs.map +1 -1
  15. package/esm/collector/report-to-trace.d.mts +2 -1
  16. package/esm/collector/report-to-trace.d.mts.map +1 -1
  17. package/esm/collector/report-to-trace.mjs +2 -2
  18. package/esm/collector/report-to-trace.mjs.map +1 -1
  19. package/esm/contracts/trace.type.d.mts +22 -3
  20. package/esm/contracts/trace.type.d.mts.map +1 -1
  21. package/esm/exporters/console/console-exporter.d.mts +11 -3
  22. package/esm/exporters/console/console-exporter.d.mts.map +1 -1
  23. package/esm/exporters/console/console-exporter.mjs +31 -13
  24. package/esm/exporters/console/console-exporter.mjs.map +1 -1
  25. package/esm/exporters/console/console-exporter.type.d.mts +15 -0
  26. package/esm/exporters/console/console-exporter.type.d.mts.map +1 -1
  27. package/esm/exporters/console/format-span-io.d.mts +27 -0
  28. package/esm/exporters/console/format-span-io.d.mts.map +1 -0
  29. package/esm/exporters/console/format-span-io.mjs +52 -0
  30. package/esm/exporters/console/format-span-io.mjs.map +1 -0
  31. package/esm/exporters/console/index.d.mts +2 -1
  32. package/esm/exporters/index.d.mts +1 -0
  33. package/esm/exporters/index.mjs +1 -0
  34. package/esm/exporters/langfuse/langfuse-exporter.d.mts +10 -6
  35. package/esm/exporters/langfuse/langfuse-exporter.d.mts.map +1 -1
  36. package/esm/exporters/langfuse/langfuse-exporter.mjs +77 -30
  37. package/esm/exporters/langfuse/langfuse-exporter.mjs.map +1 -1
  38. package/esm/exporters/langfuse/langfuse-exporter.type.d.mts +3 -1
  39. package/esm/exporters/langfuse/langfuse-exporter.type.d.mts.map +1 -1
  40. package/esm/exporters/otel/otel-exporter.mjs +15 -0
  41. package/esm/exporters/otel/otel-exporter.mjs.map +1 -1
  42. package/esm/exporters/utils/gen-ai-attributes.d.mts +3 -1
  43. package/esm/exporters/utils/gen-ai-attributes.d.mts.map +1 -1
  44. package/esm/exporters/utils/gen-ai-attributes.mjs +5 -1
  45. package/esm/exporters/utils/gen-ai-attributes.mjs.map +1 -1
  46. package/esm/index.d.mts +3 -1
  47. package/esm/index.mjs +2 -1
  48. package/esm/panoptic/panoptic.d.mts.map +1 -1
  49. package/esm/panoptic/panoptic.mjs +4 -1
  50. package/esm/panoptic/panoptic.mjs.map +1 -1
  51. package/esm/panoptic/panoptic.type.d.mts +17 -0
  52. package/esm/panoptic/panoptic.type.d.mts.map +1 -1
  53. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { TraceSpan } from "../contracts/trace.type.mjs";
2
+ import { ContentCaptureOptions } from "./content-capture.type.mjs";
2
3
  import { BaseReport } from "@warlock.js/ai";
3
4
 
4
5
  //#region ../@warlock.js/ai-panoptic/src/collector/report-to-span.d.ts
@@ -17,6 +18,12 @@ import { BaseReport } from "@warlock.js/ai";
17
18
  * identity, tool trip index) is routed into the optional `attributes`
18
19
  * bag via {@link extractSpanAttributes}.
19
20
  *
21
+ * When {@link ContentCaptureOptions.captureContent} is set, the raw
22
+ * prompt/response (agents) and args/result (tools) are additionally
23
+ * copied onto `span.input` / `span.output` — off by default because
24
+ * payloads are large and often sensitive. A {@link ContentRedactor} can
25
+ * mask each value first.
26
+ *
20
27
  * No external lookup is needed — a `BaseReport` already carries
21
28
  * everything a span requires, so a collector can flatten a tree without
22
29
  * consulting any other source.
@@ -25,7 +32,7 @@ import { BaseReport } from "@warlock.js/ai";
25
32
  * const root = reportToSpan(result.report);
26
33
  * console.log(root.spanId, root.traceId, root.children.length);
27
34
  */
28
- declare function reportToSpan(report: BaseReport): TraceSpan;
35
+ declare function reportToSpan(report: BaseReport, options?: ContentCaptureOptions): TraceSpan;
29
36
  //#endregion
30
37
  export { reportToSpan };
31
38
  //# sourceMappingURL=report-to-span.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"report-to-span.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-span.ts"],"mappings":";;;;;;AA4BA;;;;;;;;AAA2D;;;;;;;;;;;;;iBAA3C,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,SAAS"}
1
+ {"version":3,"file":"report-to-span.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-span.ts"],"mappings":";;;;;;;AAmCA;;;;;;;;;;;;;;AAA4F;;;;;;;;;;;;;iBAA5E,YAAA,CAAa,MAAA,EAAQ,UAAA,EAAY,OAAA,GAAU,qBAAA,GAAwB,SAAA"}
@@ -17,6 +17,12 @@ import { extractSpanAttributes } from "./extract-span-attributes.mjs";
17
17
  * identity, tool trip index) is routed into the optional `attributes`
18
18
  * bag via {@link extractSpanAttributes}.
19
19
  *
20
+ * When {@link ContentCaptureOptions.captureContent} is set, the raw
21
+ * prompt/response (agents) and args/result (tools) are additionally
22
+ * copied onto `span.input` / `span.output` — off by default because
23
+ * payloads are large and often sensitive. A {@link ContentRedactor} can
24
+ * mask each value first.
25
+ *
20
26
  * No external lookup is needed — a `BaseReport` already carries
21
27
  * everything a span requires, so a collector can flatten a tree without
22
28
  * consulting any other source.
@@ -25,7 +31,7 @@ import { extractSpanAttributes } from "./extract-span-attributes.mjs";
25
31
  * const root = reportToSpan(result.report);
26
32
  * console.log(root.spanId, root.traceId, root.children.length);
27
33
  */
28
- function reportToSpan(report) {
34
+ function reportToSpan(report, options) {
29
35
  const span = {
30
36
  spanId: report.runId,
31
37
  traceId: report.rootRunId,
@@ -36,7 +42,7 @@ function reportToSpan(report) {
36
42
  endedAt: report.endedAt,
37
43
  duration: report.duration,
38
44
  usage: report.usage,
39
- children: report.children.map(reportToSpan)
45
+ children: report.children.map((child) => reportToSpan(child, options))
40
46
  };
41
47
  if (report.parentRunId !== void 0) span.parentSpanId = report.parentRunId;
42
48
  if (report.sessionId !== void 0) span.sessionId = report.sessionId;
@@ -45,8 +51,66 @@ function reportToSpan(report) {
45
51
  if (error !== void 0) span.error = error;
46
52
  const attributes = extractSpanAttributes(report);
47
53
  if (attributes !== void 0) span.attributes = attributes;
54
+ if (options?.captureContent) captureContent(span, report, options.redactContent);
48
55
  return span;
49
56
  }
57
+ /**
58
+ * Copy the node's raw content onto `span.input` / `span.output`.
59
+ *
60
+ * - Tools carry the call arguments + return value directly on the report
61
+ * (`ToolCall.input` / `ToolCall.output`).
62
+ * - Agents carry a `trips[]` history; the first trip's `input` is the
63
+ * real user prompt, and the last NON-EMPTY trip `output` is the final
64
+ * response text. (A failed / max-trips run can end on a trip whose
65
+ * `output` is `""` or tool-call-only, so we scan back for the last one
66
+ * that carried text rather than surfacing an empty string. Intermediate
67
+ * trips also store a `"[tool results]"` placeholder upstream.)
68
+ *
69
+ * Each value is passed through the optional {@link ContentRedactor};
70
+ * a redactor returning `undefined` drops the field.
71
+ */
72
+ function captureContent(span, report, redact) {
73
+ const node = report;
74
+ let input;
75
+ let output;
76
+ if (report.type === "tool") {
77
+ input = node.input;
78
+ output = node.output;
79
+ } else if (Array.isArray(node.trips) && node.trips.length > 0) {
80
+ input = node.trips[0]?.input;
81
+ output = lastNonEmptyOutput(node.trips);
82
+ }
83
+ if (input !== void 0) {
84
+ const value = redact ? redact(input, {
85
+ name: span.name,
86
+ type: span.type,
87
+ field: "input"
88
+ }) : input;
89
+ if (value !== void 0) span.input = value;
90
+ }
91
+ if (output !== void 0) {
92
+ const value = redact ? redact(output, {
93
+ name: span.name,
94
+ type: span.type,
95
+ field: "output"
96
+ }) : output;
97
+ if (value !== void 0) span.output = value;
98
+ }
99
+ }
100
+ /**
101
+ * The last trip output that actually carries text — the agent's final
102
+ * response on the happy path. A failed or max-trips run can end on a trip
103
+ * whose `output` is `""` or tool-call-only, so we scan backwards for the
104
+ * last trip that produced text rather than blindly taking the final trip
105
+ * (which would surface an empty string). Returns `undefined` when no trip
106
+ * produced any output.
107
+ */
108
+ function lastNonEmptyOutput(trips) {
109
+ for (let i = trips.length - 1; i >= 0; i -= 1) {
110
+ const out = trips[i]?.output;
111
+ if (typeof out === "string" ? out.length > 0 : out !== void 0) return out;
112
+ }
113
+ }
50
114
 
51
115
  //#endregion
52
116
  export { reportToSpan };
@@ -1 +1 @@
1
- {"version":3,"file":"report-to-span.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-span.ts"],"sourcesContent":["import type { BaseReport } from \"@warlock.js/ai\";\nimport type { TraceSpan } from \"../contracts/trace.type\";\nimport { extractSpanAttributes } from \"./extract-span-attributes\";\nimport { normalizeError } from \"./normalize-error\";\n\n/**\n * Project one {@link BaseReport} node — and its entire subtree — into a\n * {@link TraceSpan}. Pure and recursive: identity, timing, status, and\n * the rolled-up `usage` map across 1:1 from the report; lineage maps\n * `runId → spanId`, `parentRunId → parentSpanId`, `rootRunId → traceId`;\n * children recurse in invocation order so the span tree mirrors the\n * report tree exactly.\n *\n * The error is normalized to the JSON-safe {@link\n * import(\"../contracts/trace.type\").TraceSpanError} shape only when the\n * node carried one (failed / cancelled). Primitive-specific detail that\n * has no first-class span field (trip / step / iteration counts, model\n * identity, tool trip index) is routed into the optional `attributes`\n * bag via {@link extractSpanAttributes}.\n *\n * No external lookup is needed — a `BaseReport` already carries\n * everything a span requires, so a collector can flatten a tree without\n * consulting any other source.\n *\n * @example\n * const root = reportToSpan(result.report);\n * console.log(root.spanId, root.traceId, root.children.length);\n */\nexport function reportToSpan(report: BaseReport): TraceSpan {\n const span: TraceSpan = {\n spanId: report.runId,\n traceId: report.rootRunId,\n name: report.name,\n type: report.type,\n status: report.status,\n startedAt: report.startedAt,\n endedAt: report.endedAt,\n duration: report.duration,\n usage: report.usage,\n children: report.children.map(reportToSpan),\n };\n\n if (report.parentRunId !== undefined) {\n span.parentSpanId = report.parentRunId;\n }\n\n if (report.sessionId !== undefined) {\n span.sessionId = report.sessionId;\n }\n\n if (report.version !== undefined) {\n span.version = report.version;\n }\n\n const error = normalizeError((report as { error?: unknown }).error);\n if (error !== undefined) {\n span.error = error;\n }\n\n const attributes = extractSpanAttributes(report);\n if (attributes !== undefined) {\n span.attributes = attributes;\n }\n\n return span;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,aAAa,QAA+B;CAC1D,MAAM,OAAkB;EACtB,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,MAAM,OAAO;EACb,MAAM,OAAO;EACb,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,OAAO,OAAO;EACd,UAAU,OAAO,SAAS,IAAI,YAAY;CAC5C;CAEA,IAAI,OAAO,gBAAgB,QACzB,KAAK,eAAe,OAAO;CAG7B,IAAI,OAAO,cAAc,QACvB,KAAK,YAAY,OAAO;CAG1B,IAAI,OAAO,YAAY,QACrB,KAAK,UAAU,OAAO;CAGxB,MAAM,QAAQ,eAAgB,OAA+B,KAAK;CAClE,IAAI,UAAU,QACZ,KAAK,QAAQ;CAGf,MAAM,aAAa,sBAAsB,MAAM;CAC/C,IAAI,eAAe,QACjB,KAAK,aAAa;CAGpB,OAAO;AACT"}
1
+ {"version":3,"file":"report-to-span.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-span.ts"],"sourcesContent":["import type { BaseReport } from \"@warlock.js/ai\";\nimport type { TraceSpan } from \"../contracts/trace.type\";\nimport type { ContentCaptureOptions, ContentRedactor } from \"./content-capture.type\";\nimport { extractSpanAttributes } from \"./extract-span-attributes\";\nimport { normalizeError } from \"./normalize-error\";\n\n/**\n * Project one {@link BaseReport} node — and its entire subtree — into a\n * {@link TraceSpan}. Pure and recursive: identity, timing, status, and\n * the rolled-up `usage` map across 1:1 from the report; lineage maps\n * `runId → spanId`, `parentRunId → parentSpanId`, `rootRunId → traceId`;\n * children recurse in invocation order so the span tree mirrors the\n * report tree exactly.\n *\n * The error is normalized to the JSON-safe {@link\n * import(\"../contracts/trace.type\").TraceSpanError} shape only when the\n * node carried one (failed / cancelled). Primitive-specific detail that\n * has no first-class span field (trip / step / iteration counts, model\n * identity, tool trip index) is routed into the optional `attributes`\n * bag via {@link extractSpanAttributes}.\n *\n * When {@link ContentCaptureOptions.captureContent} is set, the raw\n * prompt/response (agents) and args/result (tools) are additionally\n * copied onto `span.input` / `span.output` — off by default because\n * payloads are large and often sensitive. A {@link ContentRedactor} can\n * mask each value first.\n *\n * No external lookup is needed — a `BaseReport` already carries\n * everything a span requires, so a collector can flatten a tree without\n * consulting any other source.\n *\n * @example\n * const root = reportToSpan(result.report);\n * console.log(root.spanId, root.traceId, root.children.length);\n */\nexport function reportToSpan(report: BaseReport, options?: ContentCaptureOptions): TraceSpan {\n const span: TraceSpan = {\n spanId: report.runId,\n traceId: report.rootRunId,\n name: report.name,\n type: report.type,\n status: report.status,\n startedAt: report.startedAt,\n endedAt: report.endedAt,\n duration: report.duration,\n usage: report.usage,\n children: report.children.map((child) => reportToSpan(child, options)),\n };\n\n if (report.parentRunId !== undefined) {\n span.parentSpanId = report.parentRunId;\n }\n\n if (report.sessionId !== undefined) {\n span.sessionId = report.sessionId;\n }\n\n if (report.version !== undefined) {\n span.version = report.version;\n }\n\n const error = normalizeError((report as { error?: unknown }).error);\n if (error !== undefined) {\n span.error = error;\n }\n\n const attributes = extractSpanAttributes(report);\n if (attributes !== undefined) {\n span.attributes = attributes;\n }\n\n if (options?.captureContent) {\n captureContent(span, report, options.redactContent);\n }\n\n return span;\n}\n\n/** Per-primitive content fields read off a widened report node. */\ntype ContentReport = {\n input?: unknown;\n output?: unknown;\n trips?: Array<{ input?: unknown; output?: unknown }>;\n};\n\n/**\n * Copy the node's raw content onto `span.input` / `span.output`.\n *\n * - Tools carry the call arguments + return value directly on the report\n * (`ToolCall.input` / `ToolCall.output`).\n * - Agents carry a `trips[]` history; the first trip's `input` is the\n * real user prompt, and the last NON-EMPTY trip `output` is the final\n * response text. (A failed / max-trips run can end on a trip whose\n * `output` is `\"\"` or tool-call-only, so we scan back for the last one\n * that carried text rather than surfacing an empty string. Intermediate\n * trips also store a `\"[tool results]\"` placeholder upstream.)\n *\n * Each value is passed through the optional {@link ContentRedactor};\n * a redactor returning `undefined` drops the field.\n */\nfunction captureContent(span: TraceSpan, report: BaseReport, redact?: ContentRedactor): void {\n const node = report as BaseReport & ContentReport;\n\n let input: unknown;\n let output: unknown;\n\n if (report.type === \"tool\") {\n input = node.input;\n output = node.output;\n } else if (Array.isArray(node.trips) && node.trips.length > 0) {\n input = node.trips[0]?.input;\n output = lastNonEmptyOutput(node.trips);\n }\n\n if (input !== undefined) {\n const value = redact ? redact(input, { name: span.name, type: span.type, field: \"input\" }) : input;\n if (value !== undefined) {\n span.input = value;\n }\n }\n\n if (output !== undefined) {\n const value = redact ? redact(output, { name: span.name, type: span.type, field: \"output\" }) : output;\n if (value !== undefined) {\n span.output = value;\n }\n }\n}\n\n/**\n * The last trip output that actually carries text — the agent's final\n * response on the happy path. A failed or max-trips run can end on a trip\n * whose `output` is `\"\"` or tool-call-only, so we scan backwards for the\n * last trip that produced text rather than blindly taking the final trip\n * (which would surface an empty string). Returns `undefined` when no trip\n * produced any output.\n */\nfunction lastNonEmptyOutput(trips: Array<{ output?: unknown }>): unknown {\n for (let i = trips.length - 1; i >= 0; i -= 1) {\n const out = trips[i]?.output;\n const hasText = typeof out === \"string\" ? out.length > 0 : out !== undefined;\n\n if (hasText) {\n return out;\n }\n }\n\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,aAAa,QAAoB,SAA4C;CAC3F,MAAM,OAAkB;EACtB,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,MAAM,OAAO;EACb,MAAM,OAAO;EACb,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,OAAO,OAAO;EACd,UAAU,OAAO,SAAS,KAAK,UAAU,aAAa,OAAO,OAAO,CAAC;CACvE;CAEA,IAAI,OAAO,gBAAgB,QACzB,KAAK,eAAe,OAAO;CAG7B,IAAI,OAAO,cAAc,QACvB,KAAK,YAAY,OAAO;CAG1B,IAAI,OAAO,YAAY,QACrB,KAAK,UAAU,OAAO;CAGxB,MAAM,QAAQ,eAAgB,OAA+B,KAAK;CAClE,IAAI,UAAU,QACZ,KAAK,QAAQ;CAGf,MAAM,aAAa,sBAAsB,MAAM;CAC/C,IAAI,eAAe,QACjB,KAAK,aAAa;CAGpB,IAAI,SAAS,gBACX,eAAe,MAAM,QAAQ,QAAQ,aAAa;CAGpD,OAAO;AACT;;;;;;;;;;;;;;;;AAwBA,SAAS,eAAe,MAAiB,QAAoB,QAAgC;CAC3F,MAAM,OAAO;CAEb,IAAI;CACJ,IAAI;CAEJ,IAAI,OAAO,SAAS,QAAQ;EAC1B,QAAQ,KAAK;EACb,SAAS,KAAK;CAChB,OAAO,IAAI,MAAM,QAAQ,KAAK,KAAK,KAAK,KAAK,MAAM,SAAS,GAAG;EAC7D,QAAQ,KAAK,MAAM,EAAE,EAAE;EACvB,SAAS,mBAAmB,KAAK,KAAK;CACxC;CAEA,IAAI,UAAU,QAAW;EACvB,MAAM,QAAQ,SAAS,OAAO,OAAO;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,OAAO;EAAQ,CAAC,IAAI;EAC7F,IAAI,UAAU,QACZ,KAAK,QAAQ;CAEjB;CAEA,IAAI,WAAW,QAAW;EACxB,MAAM,QAAQ,SAAS,OAAO,QAAQ;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,OAAO;EAAS,CAAC,IAAI;EAC/F,IAAI,UAAU,QACZ,KAAK,SAAS;CAElB;AACF;;;;;;;;;AAUA,SAAS,mBAAmB,OAA6C;CACvE,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAC7C,MAAM,MAAM,MAAM,EAAE,EAAE;EAGtB,IAFgB,OAAO,QAAQ,WAAW,IAAI,SAAS,IAAI,QAAQ,QAGjE,OAAO;CAEX;AAGF"}
@@ -1,4 +1,5 @@
1
1
  import { Trace } from "../contracts/trace.type.mjs";
2
+ import { ContentCaptureOptions } from "./content-capture.type.mjs";
2
3
  import { BaseReport } from "@warlock.js/ai";
3
4
 
4
5
  //#region ../@warlock.js/ai-panoptic/src/collector/report-to-trace.d.ts
@@ -29,7 +30,7 @@ import { BaseReport } from "@warlock.js/ai";
29
30
  * const trace = reportToTrace(result.report, result.error);
30
31
  * console.log(trace.traceId, trace.usage.total, trace.duration);
31
32
  */
32
- declare function reportToTrace(report: BaseReport, rootError?: unknown): Trace;
33
+ declare function reportToTrace(report: BaseReport, rootError?: unknown, options?: ContentCaptureOptions): Trace;
33
34
  //#endregion
34
35
  export { reportToTrace };
35
36
  //# sourceMappingURL=report-to-trace.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"report-to-trace.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-trace.ts"],"mappings":";;;;;;AAgCA;;;;;;;;;AAA6E;;;;;;;;;;;;;;;;iBAA7D,aAAA,CAAc,MAAA,EAAQ,UAAA,EAAY,SAAA,aAAsB,KAAK"}
1
+ {"version":3,"file":"report-to-trace.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-trace.ts"],"mappings":";;;;;;;AAiCA;;;;;;;;;;;;;;;AAIQ;;;;;;;;;;iBAJQ,aAAA,CACd,MAAA,EAAQ,UAAA,EACR,SAAA,YACA,OAAA,GAAU,qBAAA,GACT,KAAA"}
@@ -29,8 +29,8 @@ import { reportToSpan } from "./report-to-span.mjs";
29
29
  * const trace = reportToTrace(result.report, result.error);
30
30
  * console.log(trace.traceId, trace.usage.total, trace.duration);
31
31
  */
32
- function reportToTrace(report, rootError) {
33
- const root = reportToSpan(report);
32
+ function reportToTrace(report, rootError, options) {
33
+ const root = reportToSpan(report, options);
34
34
  if (root.error === void 0) {
35
35
  const error = normalizeError(rootError);
36
36
  if (error !== void 0) root.error = error;
@@ -1 +1 @@
1
- {"version":3,"file":"report-to-trace.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-trace.ts"],"sourcesContent":["import type { BaseReport } from \"@warlock.js/ai\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport { normalizeError } from \"./normalize-error\";\nimport { reportToSpan } from \"./report-to-span\";\n\n/**\n * Project an outermost {@link BaseReport} (one whole `.execute()` /\n * `.invoke()` run) into a {@link Trace} — the root {@link\n * import(\"../contracts/trace.type\").TraceSpan} plus the trace-wide\n * rollups exporters need without re-walking the tree.\n *\n * The trace-level identity and rollups all read off the root span the\n * projection already built (`traceId`, `usage`, timing), so the trace\n * envelope never disagrees with its own root. `reportSchemaVersion` is\n * mirrored from the root report when present (it is stamped only on\n * root nodes upstream) so exporters can branch on the source shape.\n *\n * Pure — the same input always yields the same trace. The collector\n * exposes this as `toTrace` so callers can inspect the normalized shape\n * without dispatching to exporters.\n *\n * The optional `rootError` threads the failing run's envelope error\n * (`BaseResult.error`) onto the root span. The error lives on the result\n * envelope, never on the `BaseReport` tree, so without this a failed root\n * would export `status` with no error type/message. Applied only when the\n * root span carries none of its own; the subtree projection stays pure\n * (each child surfaces its own report-level error, if any).\n *\n * @example\n * const trace = reportToTrace(result.report, result.error);\n * console.log(trace.traceId, trace.usage.total, trace.duration);\n */\nexport function reportToTrace(report: BaseReport, rootError?: unknown): Trace {\n const root = reportToSpan(report);\n\n if (root.error === undefined) {\n const error = normalizeError(rootError);\n\n if (error !== undefined) {\n root.error = error;\n }\n }\n\n const trace: Trace = {\n traceId: root.traceId,\n root,\n startedAt: root.startedAt,\n endedAt: root.endedAt,\n duration: root.duration,\n usage: root.usage,\n };\n\n if (root.sessionId !== undefined) {\n trace.sessionId = root.sessionId;\n }\n\n if (report.reportSchemaVersion !== undefined) {\n trace.reportSchemaVersion = report.reportSchemaVersion;\n }\n\n return trace;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,cAAc,QAAoB,WAA4B;CAC5E,MAAM,OAAO,aAAa,MAAM;CAEhC,IAAI,KAAK,UAAU,QAAW;EAC5B,MAAM,QAAQ,eAAe,SAAS;EAEtC,IAAI,UAAU,QACZ,KAAK,QAAQ;CAEjB;CAEA,MAAM,QAAe;EACnB,SAAS,KAAK;EACd;EACA,WAAW,KAAK;EAChB,SAAS,KAAK;EACd,UAAU,KAAK;EACf,OAAO,KAAK;CACd;CAEA,IAAI,KAAK,cAAc,QACrB,MAAM,YAAY,KAAK;CAGzB,IAAI,OAAO,wBAAwB,QACjC,MAAM,sBAAsB,OAAO;CAGrC,OAAO;AACT"}
1
+ {"version":3,"file":"report-to-trace.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/collector/report-to-trace.ts"],"sourcesContent":["import type { BaseReport } from \"@warlock.js/ai\";\nimport type { Trace } from \"../contracts/trace.type\";\nimport type { ContentCaptureOptions } from \"./content-capture.type\";\nimport { normalizeError } from \"./normalize-error\";\nimport { reportToSpan } from \"./report-to-span\";\n\n/**\n * Project an outermost {@link BaseReport} (one whole `.execute()` /\n * `.invoke()` run) into a {@link Trace} — the root {@link\n * import(\"../contracts/trace.type\").TraceSpan} plus the trace-wide\n * rollups exporters need without re-walking the tree.\n *\n * The trace-level identity and rollups all read off the root span the\n * projection already built (`traceId`, `usage`, timing), so the trace\n * envelope never disagrees with its own root. `reportSchemaVersion` is\n * mirrored from the root report when present (it is stamped only on\n * root nodes upstream) so exporters can branch on the source shape.\n *\n * Pure — the same input always yields the same trace. The collector\n * exposes this as `toTrace` so callers can inspect the normalized shape\n * without dispatching to exporters.\n *\n * The optional `rootError` threads the failing run's envelope error\n * (`BaseResult.error`) onto the root span. The error lives on the result\n * envelope, never on the `BaseReport` tree, so without this a failed root\n * would export `status` with no error type/message. Applied only when the\n * root span carries none of its own; the subtree projection stays pure\n * (each child surfaces its own report-level error, if any).\n *\n * @example\n * const trace = reportToTrace(result.report, result.error);\n * console.log(trace.traceId, trace.usage.total, trace.duration);\n */\nexport function reportToTrace(\n report: BaseReport,\n rootError?: unknown,\n options?: ContentCaptureOptions,\n): Trace {\n const root = reportToSpan(report, options);\n\n if (root.error === undefined) {\n const error = normalizeError(rootError);\n\n if (error !== undefined) {\n root.error = error;\n }\n }\n\n const trace: Trace = {\n traceId: root.traceId,\n root,\n startedAt: root.startedAt,\n endedAt: root.endedAt,\n duration: root.duration,\n usage: root.usage,\n };\n\n if (root.sessionId !== undefined) {\n trace.sessionId = root.sessionId;\n }\n\n if (report.reportSchemaVersion !== undefined) {\n trace.reportSchemaVersion = report.reportSchemaVersion;\n }\n\n return trace;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,cACd,QACA,WACA,SACO;CACP,MAAM,OAAO,aAAa,QAAQ,OAAO;CAEzC,IAAI,KAAK,UAAU,QAAW;EAC5B,MAAM,QAAQ,eAAe,SAAS;EAEtC,IAAI,UAAU,QACZ,KAAK,QAAQ;CAEjB;CAEA,MAAM,QAAe;EACnB,SAAS,KAAK;EACd;EACA,WAAW,KAAK;EAChB,SAAS,KAAK;EACd,UAAU,KAAK;EACf,OAAO,KAAK;CACd;CAEA,IAAI,KAAK,cAAc,QACrB,MAAM,YAAY,KAAK;CAGzB,IAAI,OAAO,wBAAwB,QACjC,MAAM,sBAAsB,OAAO;CAGrC,OAAO;AACT"}
@@ -13,7 +13,9 @@ import { ReportStatus, ReportType, Usage } from "@warlock.js/ai";
13
13
  * about — identity (`spanId` / `parentSpanId` / `traceId`), timing
14
14
  * (`startedAt` / `endedAt` / `duration`), outcome (`status` / `error`),
15
15
  * and cost (`usage`) — so a collector can flatten a `BaseReport` tree
16
- * into spans without consulting any other source.
16
+ * into spans without consulting any other source. Raw content — prompts,
17
+ * responses, and tool payloads — is captured onto `input` / `output`
18
+ * only when the collector opts in via `captureContent` (off by default).
17
19
  *
18
20
  * @example
19
21
  * const root: TraceSpan = {
@@ -75,10 +77,27 @@ type TraceSpan = {
75
77
  /**
76
78
  * Free-form attribute bag forwarded verbatim onto the backend span
77
79
  * (OTel attributes, Langfuse metadata). Collectors populate this with
78
- * primitive-specific detail (trip index, step name, tool args digest)
79
- * that has no first-class field here. Absent when empty.
80
+ * primitive-specific metadata (trip / step / iteration counts, model
81
+ * identity, the tool's originating trip index) that has no first-class
82
+ * field here. Raw prompts / completions / tool payloads do NOT live
83
+ * here — they go on {@link TraceSpan.input} / {@link TraceSpan.output}
84
+ * under content capture. Absent when empty.
80
85
  */
81
86
  attributes?: Record<string, unknown>;
87
+ /**
88
+ * Captured input for this node — an agent's first-trip prompt or a
89
+ * tool's call arguments — present ONLY when the collector ran with
90
+ * content capture enabled (`captureContent`), off by default because
91
+ * payloads are large and often sensitive. Typed `unknown`: agent
92
+ * prompts are strings, tool args are the validated argument value.
93
+ */
94
+ input?: unknown;
95
+ /**
96
+ * Captured output for this node — an agent's final response text or a
97
+ * tool's return value — present only under content capture (see
98
+ * {@link TraceSpan.input}).
99
+ */
100
+ output?: unknown;
82
101
  /**
83
102
  * Child spans, mirroring `BaseReport.children` in invocation order.
84
103
  * Empty for leaf spans (pure tools). Exporters that emit a flat
@@ -1 +1 @@
1
- {"version":3,"file":"trace.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/contracts/trace.type.ts"],"mappings":";;;;;AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;KAAY,SAAA;EAiDF;;;;EA5CR,MAAA;EAyDmB;AAAA;AAQrB;;EA5DE,YAAA;EA4DwB;;;;;AAMnB;EA3DL,OAAA;EAoEe;;;;;EA9Df,SAAA,WAoEM;EAlEN,IAAA;EAsEA;;;;;EAhEA,OAAA,WA0EmB;EAxEnB,IAAA,EAAM,UAAA;EAEN,MAAA,EAAQ,YAAA;EAER,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAA;;;;;;EAMP,KAAA,GAAQ,cAAA;;;;;;;EAOR,UAAA,GAAa,MAAA;;;;;;EAMb,QAAA,EAAU,SAAA;AAAA;;;;;;KAQA,cAAA;6EAEV,IAAA;EAEA,OAAA;EAEA,KAAA;AAAA;;;;;;;KASU,KAAA;mEAEV,OAAA;EAEA,SAAA;EAEA,IAAA,EAAM,SAAA;EAEN,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAK;;;;;;EAMZ,mBAAA;AAAA"}
1
+ {"version":3,"file":"trace.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-panoptic/src/contracts/trace.type.ts"],"mappings":";;;;;AAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAAY,SAAA;EA2DG;;;;EAtDb,MAAA;EA0EmB;AAAA;AAQrB;;EA7EE,YAAA;EA6EwB;;;;;AAMnB;EA5EL,OAAA;EAqFe;;;;;EA/Ef,SAAA,WAqFM;EAnFN,IAAA;EAuFA;;;;;EAjFA,OAAA,WA2FmB;EAzFnB,IAAA,EAAM,UAAA;EAEN,MAAA,EAAQ,YAAA;EAER,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAA;;;;;;EAMP,KAAA,GAAQ,cAAA;;;;;;;;;;EAUR,UAAA,GAAa,MAAA;;;;;;;;EAQb,KAAA;;;;;;EAMA,MAAA;;;;;;EAMA,QAAA,EAAU,SAAA;AAAA;;;;;;KAQA,cAAA;6EAEV,IAAA;EAEA,OAAA;EAEA,KAAA;AAAA;;;;;;;KASU,KAAA;mEAEV,OAAA;EAEA,SAAA;EAEA,IAAA,EAAM,SAAA;EAEN,SAAA;EAEA,OAAA;EAEA,QAAA;EAEA,KAAA,EAAO,KAAK;;;;;;EAMZ,mBAAA;AAAA"}
@@ -8,9 +8,10 @@ import { ConsoleExporterOptions } from "./console-exporter.type.mjs";
8
8
  * as the reference implementation of the contract.
9
9
  *
10
10
  * By default it prints one summary line per completed trace. Set
11
- * `tree: true` to print the full indented span tree, and
12
- * `streaming: true` to also print each span the moment it finalizes
13
- * (via the optional `exportSpan` hook).
11
+ * `tree: true` to print the full indented span tree, `io: true` to also
12
+ * print each span's captured `input` / `output` (needs the collector's
13
+ * `captureContent`), and `streaming: true` to print each span the moment
14
+ * it finalizes (via the optional `exportSpan` hook).
14
15
  *
15
16
  * @example
16
17
  * collector.use(consoleExporter());
@@ -18,6 +19,13 @@ import { ConsoleExporterOptions } from "./console-exporter.type.mjs";
18
19
  *
19
20
  * @example
20
21
  * collector.use(consoleExporter({ tree: true }));
22
+ *
23
+ * @example
24
+ * // Full content trace — prompts, responses, and tool I/O:
25
+ * const observe = panoptic({
26
+ * captureContent: true,
27
+ * exporters: [consoleExporter({ tree: true, io: true })],
28
+ * });
21
29
  */
22
30
  declare function consoleExporter(options?: ConsoleExporterOptions): ExporterContract;
23
31
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"console-exporter.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.ts"],"mappings":";;;;;;;AAwBA;;;;;;;;AAAuF;;;;;;iBAAvE,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,gBAAgB"}
1
+ {"version":3,"file":"console-exporter.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.ts"],"mappings":";;;;;;;AAiCA;;;;;;;;AAAuF;;;;;;;;;;;;;;iBAAvE,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,gBAAgB"}
@@ -1,4 +1,5 @@
1
1
  import { walkSpans } from "../utils/walk-spans.mjs";
2
+ import { formatSpanIO } from "./format-span-io.mjs";
2
3
  import { formatSpanLine } from "./format-span-line.mjs";
3
4
 
4
5
  //#region ../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.ts
@@ -9,9 +10,10 @@ const EXPORTER_NAME = "console";
9
10
  * as the reference implementation of the contract.
10
11
  *
11
12
  * By default it prints one summary line per completed trace. Set
12
- * `tree: true` to print the full indented span tree, and
13
- * `streaming: true` to also print each span the moment it finalizes
14
- * (via the optional `exportSpan` hook).
13
+ * `tree: true` to print the full indented span tree, `io: true` to also
14
+ * print each span's captured `input` / `output` (needs the collector's
15
+ * `captureContent`), and `streaming: true` to print each span the moment
16
+ * it finalizes (via the optional `exportSpan` hook).
15
17
  *
16
18
  * @example
17
19
  * collector.use(consoleExporter());
@@ -19,36 +21,52 @@ const EXPORTER_NAME = "console";
19
21
  *
20
22
  * @example
21
23
  * collector.use(consoleExporter({ tree: true }));
24
+ *
25
+ * @example
26
+ * // Full content trace — prompts, responses, and tool I/O:
27
+ * const observe = panoptic({
28
+ * captureContent: true,
29
+ * exporters: [consoleExporter({ tree: true, io: true })],
30
+ * });
22
31
  */
23
32
  function consoleExporter(options = {}) {
24
33
  const sink = options.console ?? console;
25
34
  const tree = options.tree ?? false;
35
+ const io = options.io ?? false;
36
+ const ioMaxChars = options.ioMaxChars;
26
37
  const exporter = {
27
38
  name: EXPORTER_NAME,
28
39
  export(trace) {
29
- writeTrace(sink, trace, tree);
40
+ writeTrace(sink, trace, tree, io, ioMaxChars);
30
41
  }
31
42
  };
32
43
  if (options.streaming) exporter.exportSpan = (span) => {
33
44
  sink.log(formatSpanLine(span));
45
+ if (io) for (const line of formatSpanIO(span, 0, ioMaxChars)) sink.log(line);
34
46
  };
35
47
  return exporter;
36
48
  }
37
49
  /**
38
50
  * Write a completed trace — either a single root summary line or the
39
- * full indented tree. Failed traces are routed to `console.error` so
40
- * they surface at the right severity in log aggregators.
51
+ * full indented tree, each span optionally followed by its captured
52
+ * `input` / `output`. Failed / cancelled spans route to `console.error`
53
+ * so they surface at the right severity in log aggregators.
41
54
  */
42
- function writeTrace(sink, trace, tree) {
55
+ function writeTrace(sink, trace, tree, io, ioMaxChars) {
43
56
  if (!tree) {
44
- const line = formatSpanLine(trace.root);
45
- writeAtSeverity(sink, trace.root.status, line);
57
+ writeSpan(sink, trace.root, 0, io, ioMaxChars);
46
58
  return;
47
59
  }
48
- for (const span of walkSpans(trace.root)) {
49
- const line = formatSpanLine(span, spanDepth(trace.root, span.spanId));
50
- writeAtSeverity(sink, span.status, line);
51
- }
60
+ for (const span of walkSpans(trace.root)) writeSpan(sink, span, spanDepth(trace.root, span.spanId), io, ioMaxChars);
61
+ }
62
+ /**
63
+ * Write one span's line and — when `io` is on — its captured content,
64
+ * all routed at the span's own severity so a failed span keeps its
65
+ * content beside it in the error stream.
66
+ */
67
+ function writeSpan(sink, span, depth, io, ioMaxChars) {
68
+ writeAtSeverity(sink, span.status, formatSpanLine(span, depth));
69
+ if (io) for (const line of formatSpanIO(span, depth, ioMaxChars)) writeAtSeverity(sink, span.status, line);
52
70
  }
53
71
  /**
54
72
  * Route a line to `error` when the span failed/cancelled, otherwise to
@@ -1 +1 @@
1
- {"version":3,"file":"console-exporter.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.ts"],"sourcesContent":["import type { ExporterContract, Trace, TraceSpan } from \"../../contracts\";\nimport { walkSpans } from \"../utils\";\nimport type { ConsoleExporterOptions, ConsoleLike } from \"./console-exporter.type\";\nimport { formatSpanLine } from \"./format-span-line\";\n\nconst EXPORTER_NAME = \"console\";\n\n/**\n * Zero-dependency {@link ExporterContract} that prints traces to a\n * console-like sink. The simplest exporter — useful in development and\n * as the reference implementation of the contract.\n *\n * By default it prints one summary line per completed trace. Set\n * `tree: true` to print the full indented span tree, and\n * `streaming: true` to also print each span the moment it finalizes\n * (via the optional `exportSpan` hook).\n *\n * @example\n * collector.use(consoleExporter());\n * // ok workflow \"checkout\" — 2103ms, 1820 tok, $0.0094\n *\n * @example\n * collector.use(consoleExporter({ tree: true }));\n */\nexport function consoleExporter(options: ConsoleExporterOptions = {}): ExporterContract {\n const sink: ConsoleLike = options.console ?? console;\n const tree = options.tree ?? false;\n\n const exporter: ExporterContract = {\n name: EXPORTER_NAME,\n export(trace: Trace): void {\n writeTrace(sink, trace, tree);\n },\n };\n\n if (options.streaming) {\n exporter.exportSpan = (span: TraceSpan): void => {\n sink.log(formatSpanLine(span));\n };\n }\n\n return exporter;\n}\n\n/**\n * Write a completed trace — either a single root summary line or the\n * full indented tree. Failed traces are routed to `console.error` so\n * they surface at the right severity in log aggregators.\n */\nfunction writeTrace(sink: ConsoleLike, trace: Trace, tree: boolean): void {\n if (!tree) {\n const line = formatSpanLine(trace.root);\n writeAtSeverity(sink, trace.root.status, line);\n return;\n }\n\n for (const span of walkSpans(trace.root)) {\n const depth = spanDepth(trace.root, span.spanId);\n const line = formatSpanLine(span, depth);\n writeAtSeverity(sink, span.status, line);\n }\n}\n\n/**\n * Route a line to `error` when the span failed/cancelled, otherwise to\n * `log`. Keeps healthy traces out of the error stream.\n */\nfunction writeAtSeverity(sink: ConsoleLike, status: TraceSpan[\"status\"], line: string): void {\n if (status === \"failed\" || status === \"cancelled\") {\n sink.error(line);\n return;\n }\n\n sink.log(line);\n}\n\n/**\n * Depth of `targetSpanId` below `root` for indentation. Walks the tree\n * once; returns 0 when the span is the root or not found.\n */\nfunction spanDepth(root: TraceSpan, targetSpanId: string, depth = 0): number {\n if (root.spanId === targetSpanId) {\n return depth;\n }\n\n for (const child of root.children) {\n const found = spanDepth(child, targetSpanId, depth + 1);\n\n if (found > 0) {\n return found;\n }\n }\n\n return 0;\n}\n"],"mappings":";;;;AAKA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;AAmBtB,SAAgB,gBAAgB,UAAkC,CAAC,GAAqB;CACtF,MAAM,OAAoB,QAAQ,WAAW;CAC7C,MAAM,OAAO,QAAQ,QAAQ;CAE7B,MAAM,WAA6B;EACjC,MAAM;EACN,OAAO,OAAoB;GACzB,WAAW,MAAM,OAAO,IAAI;EAC9B;CACF;CAEA,IAAI,QAAQ,WACV,SAAS,cAAc,SAA0B;EAC/C,KAAK,IAAI,eAAe,IAAI,CAAC;CAC/B;CAGF,OAAO;AACT;;;;;;AAOA,SAAS,WAAW,MAAmB,OAAc,MAAqB;CACxE,IAAI,CAAC,MAAM;EACT,MAAM,OAAO,eAAe,MAAM,IAAI;EACtC,gBAAgB,MAAM,MAAM,KAAK,QAAQ,IAAI;EAC7C;CACF;CAEA,KAAK,MAAM,QAAQ,UAAU,MAAM,IAAI,GAAG;EAExC,MAAM,OAAO,eAAe,MADd,UAAU,MAAM,MAAM,KAAK,MACH,CAAC;EACvC,gBAAgB,MAAM,KAAK,QAAQ,IAAI;CACzC;AACF;;;;;AAMA,SAAS,gBAAgB,MAAmB,QAA6B,MAAoB;CAC3F,IAAI,WAAW,YAAY,WAAW,aAAa;EACjD,KAAK,MAAM,IAAI;EACf;CACF;CAEA,KAAK,IAAI,IAAI;AACf;;;;;AAMA,SAAS,UAAU,MAAiB,cAAsB,QAAQ,GAAW;CAC3E,IAAI,KAAK,WAAW,cAClB,OAAO;CAGT,KAAK,MAAM,SAAS,KAAK,UAAU;EACjC,MAAM,QAAQ,UAAU,OAAO,cAAc,QAAQ,CAAC;EAEtD,IAAI,QAAQ,GACV,OAAO;CAEX;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"console-exporter.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.ts"],"sourcesContent":["import type { ExporterContract, Trace, TraceSpan } from \"../../contracts\";\nimport { walkSpans } from \"../utils\";\nimport type { ConsoleExporterOptions, ConsoleLike } from \"./console-exporter.type\";\nimport { formatSpanIO } from \"./format-span-io\";\nimport { formatSpanLine } from \"./format-span-line\";\n\nconst EXPORTER_NAME = \"console\";\n\n/**\n * Zero-dependency {@link ExporterContract} that prints traces to a\n * console-like sink. The simplest exporter — useful in development and\n * as the reference implementation of the contract.\n *\n * By default it prints one summary line per completed trace. Set\n * `tree: true` to print the full indented span tree, `io: true` to also\n * print each span's captured `input` / `output` (needs the collector's\n * `captureContent`), and `streaming: true` to print each span the moment\n * it finalizes (via the optional `exportSpan` hook).\n *\n * @example\n * collector.use(consoleExporter());\n * // ok workflow \"checkout\" — 2103ms, 1820 tok, $0.0094\n *\n * @example\n * collector.use(consoleExporter({ tree: true }));\n *\n * @example\n * // Full content trace — prompts, responses, and tool I/O:\n * const observe = panoptic({\n * captureContent: true,\n * exporters: [consoleExporter({ tree: true, io: true })],\n * });\n */\nexport function consoleExporter(options: ConsoleExporterOptions = {}): ExporterContract {\n const sink: ConsoleLike = options.console ?? console;\n const tree = options.tree ?? false;\n const io = options.io ?? false;\n const ioMaxChars = options.ioMaxChars;\n\n const exporter: ExporterContract = {\n name: EXPORTER_NAME,\n export(trace: Trace): void {\n writeTrace(sink, trace, tree, io, ioMaxChars);\n },\n };\n\n if (options.streaming) {\n exporter.exportSpan = (span: TraceSpan): void => {\n sink.log(formatSpanLine(span));\n\n if (io) {\n for (const line of formatSpanIO(span, 0, ioMaxChars)) {\n sink.log(line);\n }\n }\n };\n }\n\n return exporter;\n}\n\n/**\n * Write a completed trace — either a single root summary line or the\n * full indented tree, each span optionally followed by its captured\n * `input` / `output`. Failed / cancelled spans route to `console.error`\n * so they surface at the right severity in log aggregators.\n */\nfunction writeTrace(\n sink: ConsoleLike,\n trace: Trace,\n tree: boolean,\n io: boolean,\n ioMaxChars: number | undefined,\n): void {\n if (!tree) {\n writeSpan(sink, trace.root, 0, io, ioMaxChars);\n return;\n }\n\n for (const span of walkSpans(trace.root)) {\n const depth = spanDepth(trace.root, span.spanId);\n writeSpan(sink, span, depth, io, ioMaxChars);\n }\n}\n\n/**\n * Write one span's line and — when `io` is on — its captured content,\n * all routed at the span's own severity so a failed span keeps its\n * content beside it in the error stream.\n */\nfunction writeSpan(\n sink: ConsoleLike,\n span: TraceSpan,\n depth: number,\n io: boolean,\n ioMaxChars: number | undefined,\n): void {\n writeAtSeverity(sink, span.status, formatSpanLine(span, depth));\n\n if (io) {\n for (const line of formatSpanIO(span, depth, ioMaxChars)) {\n writeAtSeverity(sink, span.status, line);\n }\n }\n}\n\n/**\n * Route a line to `error` when the span failed/cancelled, otherwise to\n * `log`. Keeps healthy traces out of the error stream.\n */\nfunction writeAtSeverity(sink: ConsoleLike, status: TraceSpan[\"status\"], line: string): void {\n if (status === \"failed\" || status === \"cancelled\") {\n sink.error(line);\n return;\n }\n\n sink.log(line);\n}\n\n/**\n * Depth of `targetSpanId` below `root` for indentation. Walks the tree\n * once; returns 0 when the span is the root or not found.\n */\nfunction spanDepth(root: TraceSpan, targetSpanId: string, depth = 0): number {\n if (root.spanId === targetSpanId) {\n return depth;\n }\n\n for (const child of root.children) {\n const found = spanDepth(child, targetSpanId, depth + 1);\n\n if (found > 0) {\n return found;\n }\n }\n\n return 0;\n}\n"],"mappings":";;;;;AAMA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BtB,SAAgB,gBAAgB,UAAkC,CAAC,GAAqB;CACtF,MAAM,OAAoB,QAAQ,WAAW;CAC7C,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,KAAK,QAAQ,MAAM;CACzB,MAAM,aAAa,QAAQ;CAE3B,MAAM,WAA6B;EACjC,MAAM;EACN,OAAO,OAAoB;GACzB,WAAW,MAAM,OAAO,MAAM,IAAI,UAAU;EAC9C;CACF;CAEA,IAAI,QAAQ,WACV,SAAS,cAAc,SAA0B;EAC/C,KAAK,IAAI,eAAe,IAAI,CAAC;EAE7B,IAAI,IACF,KAAK,MAAM,QAAQ,aAAa,MAAM,GAAG,UAAU,GACjD,KAAK,IAAI,IAAI;CAGnB;CAGF,OAAO;AACT;;;;;;;AAQA,SAAS,WACP,MACA,OACA,MACA,IACA,YACM;CACN,IAAI,CAAC,MAAM;EACT,UAAU,MAAM,MAAM,MAAM,GAAG,IAAI,UAAU;EAC7C;CACF;CAEA,KAAK,MAAM,QAAQ,UAAU,MAAM,IAAI,GAErC,UAAU,MAAM,MADF,UAAU,MAAM,MAAM,KAAK,MACf,GAAG,IAAI,UAAU;AAE/C;;;;;;AAOA,SAAS,UACP,MACA,MACA,OACA,IACA,YACM;CACN,gBAAgB,MAAM,KAAK,QAAQ,eAAe,MAAM,KAAK,CAAC;CAE9D,IAAI,IACF,KAAK,MAAM,QAAQ,aAAa,MAAM,OAAO,UAAU,GACrD,gBAAgB,MAAM,KAAK,QAAQ,IAAI;AAG7C;;;;;AAMA,SAAS,gBAAgB,MAAmB,QAA6B,MAAoB;CAC3F,IAAI,WAAW,YAAY,WAAW,aAAa;EACjD,KAAK,MAAM,IAAI;EACf;CACF;CAEA,KAAK,IAAI,IAAI;AACf;;;;;AAMA,SAAS,UAAU,MAAiB,cAAsB,QAAQ,GAAW;CAC3E,IAAI,KAAK,WAAW,cAClB,OAAO;CAGT,KAAK,MAAM,SAAS,KAAK,UAAU;EACjC,MAAM,QAAQ,UAAU,OAAO,cAAc,QAAQ,CAAC;EAEtD,IAAI,QAAQ,GACV,OAAO;CAEX;CAEA,OAAO;AACT"}
@@ -30,6 +30,21 @@ type ConsoleExporterOptions = {
30
30
  * present, so leaving this off omits the method entirely.
31
31
  */
32
32
  streaming?: boolean;
33
+ /**
34
+ * Also print each span's captured content — `TraceSpan.input` /
35
+ * `output` — as extra indented `in:` / `out:` lines beneath the span.
36
+ * Defaults to `false`. Requires the collector to run with content
37
+ * capture (`captureContent`); without it spans carry no content and
38
+ * this is a no-op.
39
+ */
40
+ io?: boolean;
41
+ /**
42
+ * Max characters of captured content shown per `in:` / `out:` line
43
+ * before truncation — only when {@link ConsoleExporterOptions.io} is on.
44
+ * Defaults to `500`. Raise it for fuller payloads, or use the file
45
+ * exporter for untruncated content.
46
+ */
47
+ ioMaxChars?: number;
33
48
  };
34
49
  //#endregion
35
50
  export { ConsoleExporterOptions, ConsoleLike };
@@ -1 +1 @@
1
- {"version":3,"file":"console-exporter.type.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.type.ts"],"mappings":";;AAMA;;;;;KAAY,WAAA;EACV,GAAA,IAAO,IAAA;EACP,KAAA,IAAS,IAAA;AAAA;AAAI;AAMf;;AANe,KAMH,sBAAA;EAKW;;;;EAArB,OAAA,GAAU,WAAW;EAYZ;AAAA;;;;EANT,IAAA;;;;;;EAMA,SAAA;AAAA"}
1
+ {"version":3,"file":"console-exporter.type.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/console-exporter.type.ts"],"mappings":";;AAMA;;;;;KAAY,WAAA;EACV,GAAA,IAAO,IAAA;EACP,KAAA,IAAS,IAAA;AAAA;AAAI;AAMf;;AANe,KAMH,sBAAA;EAKW;;;;EAArB,OAAA,GAAU,WAAW;EAoBrB;;;AAOU;;EArBV,IAAA;;;;;;EAMA,SAAA;;;;;;;;EAQA,EAAA;;;;;;;EAOA,UAAA;AAAA"}
@@ -0,0 +1,27 @@
1
+ import { TraceSpan } from "../../contracts/trace.type.mjs";
2
+ //#region ../@warlock.js/ai-panoptic/src/exporters/console/format-span-io.d.ts
3
+ /**
4
+ * Render a span's captured content ({@link TraceSpan.input} /
5
+ * {@link TraceSpan.output}) as extra indented console lines, one level
6
+ * below the span's own line:
7
+ *
8
+ * ```text
9
+ * ok agent "market-research" — 1794ms, 224 tok, $0.0008
10
+ * in: Research the market for Acme Coffee Roasters …
11
+ * out: Demand is steady; specialty buyers skew premium …
12
+ * ```
13
+ *
14
+ * Returns `[]` when the span carries no content — capture disabled, or a
15
+ * composite node with no own I/O. Each value is stringified (JSON for
16
+ * non-strings), whitespace-collapsed to stay scannable, and truncated to
17
+ * `maxChars` (default {@link DEFAULT_IO_MAX_CHARS}) with an ellipsis. Use
18
+ * the file exporter for the full, untruncated payload.
19
+ *
20
+ * @example
21
+ * formatSpanIO(toolSpan, 1);
22
+ * // [' in: {"query":"specialty coffee demand"}', ' out: {"results":[…]}']
23
+ */
24
+ declare function formatSpanIO(span: TraceSpan, depth?: number, maxChars?: number): string[];
25
+ //#endregion
26
+ export { formatSpanIO };
27
+ //# sourceMappingURL=format-span-io.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-span-io.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/format-span-io.ts"],"mappings":";;;;;AA0BA;;;;;;;;;AAAwF;;;;;;;;;iBAAxE,YAAA,CAAa,IAAA,EAAM,SAAS,EAAE,KAAA,WAAW,QAAA"}
@@ -0,0 +1,52 @@
1
+ //#region ../@warlock.js/ai-panoptic/src/exporters/console/format-span-io.ts
2
+ /** Default cap on how many characters of captured content a console line shows. */
3
+ const DEFAULT_IO_MAX_CHARS = 500;
4
+ /**
5
+ * Render a span's captured content ({@link TraceSpan.input} /
6
+ * {@link TraceSpan.output}) as extra indented console lines, one level
7
+ * below the span's own line:
8
+ *
9
+ * ```text
10
+ * ok agent "market-research" — 1794ms, 224 tok, $0.0008
11
+ * in: Research the market for Acme Coffee Roasters …
12
+ * out: Demand is steady; specialty buyers skew premium …
13
+ * ```
14
+ *
15
+ * Returns `[]` when the span carries no content — capture disabled, or a
16
+ * composite node with no own I/O. Each value is stringified (JSON for
17
+ * non-strings), whitespace-collapsed to stay scannable, and truncated to
18
+ * `maxChars` (default {@link DEFAULT_IO_MAX_CHARS}) with an ellipsis. Use
19
+ * the file exporter for the full, untruncated payload.
20
+ *
21
+ * @example
22
+ * formatSpanIO(toolSpan, 1);
23
+ * // [' in: {"query":"specialty coffee demand"}', ' out: {"results":[…]}']
24
+ */
25
+ function formatSpanIO(span, depth = 0, maxChars = DEFAULT_IO_MAX_CHARS) {
26
+ const lines = [];
27
+ const indent = " ".repeat(depth + 1);
28
+ if (span.input !== void 0) lines.push(`${indent}in: ${preview(span.input, maxChars)}`);
29
+ if (span.output !== void 0) lines.push(`${indent}out: ${preview(span.output, maxChars)}`);
30
+ return lines;
31
+ }
32
+ /**
33
+ * One-line, length-capped preview of a captured value. Strings pass
34
+ * through; everything else is JSON-stringified (falling back to
35
+ * `String()` on a circular / unstringifiable value). Internal whitespace
36
+ * is collapsed so the preview never breaks the tree layout.
37
+ */
38
+ function preview(value, maxChars) {
39
+ const collapsed = (typeof value === "string" ? value : stringify(value)).replace(/\s+/g, " ").trim();
40
+ return collapsed.length > maxChars ? `${collapsed.slice(0, maxChars)}…` : collapsed;
41
+ }
42
+ function stringify(value) {
43
+ try {
44
+ return JSON.stringify(value) ?? String(value);
45
+ } catch {
46
+ return String(value);
47
+ }
48
+ }
49
+
50
+ //#endregion
51
+ export { formatSpanIO };
52
+ //# sourceMappingURL=format-span-io.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-span-io.mjs","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/console/format-span-io.ts"],"sourcesContent":["import type { TraceSpan } from \"../../contracts\";\n\n/** Default cap on how many characters of captured content a console line shows. */\nconst DEFAULT_IO_MAX_CHARS = 500;\n\n/**\n * Render a span's captured content ({@link TraceSpan.input} /\n * {@link TraceSpan.output}) as extra indented console lines, one level\n * below the span's own line:\n *\n * ```text\n * ok agent \"market-research\" — 1794ms, 224 tok, $0.0008\n * in: Research the market for Acme Coffee Roasters …\n * out: Demand is steady; specialty buyers skew premium …\n * ```\n *\n * Returns `[]` when the span carries no content — capture disabled, or a\n * composite node with no own I/O. Each value is stringified (JSON for\n * non-strings), whitespace-collapsed to stay scannable, and truncated to\n * `maxChars` (default {@link DEFAULT_IO_MAX_CHARS}) with an ellipsis. Use\n * the file exporter for the full, untruncated payload.\n *\n * @example\n * formatSpanIO(toolSpan, 1);\n * // [' in: {\"query\":\"specialty coffee demand\"}', ' out: {\"results\":[…]}']\n */\nexport function formatSpanIO(span: TraceSpan, depth = 0, maxChars = DEFAULT_IO_MAX_CHARS): string[] {\n const lines: string[] = [];\n const indent = \" \".repeat(depth + 1);\n\n if (span.input !== undefined) {\n lines.push(`${indent}in: ${preview(span.input, maxChars)}`);\n }\n\n if (span.output !== undefined) {\n lines.push(`${indent}out: ${preview(span.output, maxChars)}`);\n }\n\n return lines;\n}\n\n/**\n * One-line, length-capped preview of a captured value. Strings pass\n * through; everything else is JSON-stringified (falling back to\n * `String()` on a circular / unstringifiable value). Internal whitespace\n * is collapsed so the preview never breaks the tree layout.\n */\nfunction preview(value: unknown, maxChars: number): string {\n const text = typeof value === \"string\" ? value : stringify(value);\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n\n return collapsed.length > maxChars ? `${collapsed.slice(0, maxChars)}…` : collapsed;\n}\n\nfunction stringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":";;AAGA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;AAuB7B,SAAgB,aAAa,MAAiB,QAAQ,GAAG,WAAW,sBAAgC;CAClG,MAAM,QAAkB,CAAC;CACzB,MAAM,SAAS,KAAK,OAAO,QAAQ,CAAC;CAEpC,IAAI,KAAK,UAAU,QACjB,MAAM,KAAK,GAAG,OAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ,GAAG;CAG7D,IAAI,KAAK,WAAW,QAClB,MAAM,KAAK,GAAG,OAAO,OAAO,QAAQ,KAAK,QAAQ,QAAQ,GAAG;CAG9D,OAAO;AACT;;;;;;;AAQA,SAAS,QAAQ,OAAgB,UAA0B;CAEzD,MAAM,aADO,OAAO,UAAU,WAAW,QAAQ,UAAU,KAAK,EAC1C,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAEjD,OAAO,UAAU,SAAS,WAAW,GAAG,UAAU,MAAM,GAAG,QAAQ,EAAE,KAAK;AAC5E;AAEA,SAAS,UAAU,OAAwB;CACzC,IAAI;EACF,OAAO,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK;CAC9C,QAAQ;EACN,OAAO,OAAO,KAAK;CACrB;AACF"}
@@ -1,3 +1,4 @@
1
1
  import { ConsoleExporterOptions, ConsoleLike } from "./console-exporter.type.mjs";
2
2
  import { consoleExporter } from "./console-exporter.mjs";
3
- import { formatSpanLine } from "./format-span-line.mjs";
3
+ import { formatSpanLine } from "./format-span-line.mjs";
4
+ import { formatSpanIO } from "./format-span-io.mjs";
@@ -1,6 +1,7 @@
1
1
  import { ConsoleExporterOptions, ConsoleLike } from "./console/console-exporter.type.mjs";
2
2
  import { consoleExporter } from "./console/console-exporter.mjs";
3
3
  import { formatSpanLine } from "./console/format-span-line.mjs";
4
+ import { formatSpanIO } from "./console/format-span-io.mjs";
4
5
  import { FileExporterOptions, TraceRecord } from "./file/file-exporter.type.mjs";
5
6
  import { fileExporter } from "./file/file-exporter.mjs";
6
7
  import { LangfuseClientLike, LangfuseExporterOptions, LangfuseObservationBody, LangfuseObservationEndBody, LangfuseObservationLevel, LangfuseObservationLike, LangfuseTraceBody, LangfuseTraceLike, LangfuseUsageBody } from "./langfuse/langfuse-exporter.type.mjs";
@@ -1,6 +1,7 @@
1
1
  import { walkSpans } from "./utils/walk-spans.mjs";
2
2
  import { totalCostUsd } from "./utils/total-cost.mjs";
3
3
  import { GEN_AI_ATTRIBUTES, WARLOCK_ATTRIBUTES, toGenAiAttributes } from "./utils/gen-ai-attributes.mjs";
4
+ import { formatSpanIO } from "./console/format-span-io.mjs";
4
5
  import { formatSpanLine } from "./console/format-span-line.mjs";
5
6
  import { consoleExporter } from "./console/console-exporter.mjs";
6
7
  import { fileExporter } from "./file/file-exporter.mjs";
@@ -9,12 +9,16 @@ import { LangfuseExporterOptions } from "./langfuse-exporter.type.mjs";
9
9
  * installed, and a missing SDK surfaces as a curated "install this"
10
10
  * error when the exporter first needs to build a client.
11
11
  *
12
- * The root {@link TraceSpan} becomes a Langfuse trace; each descendant
13
- * becomes a nested observation a `generation` when it produced tokens
14
- * (LLM-backed agents, supervisors), otherwise a plain `span` (tools,
15
- * callbacks). Timing, status, version, and rolled-up `usage` are mapped
16
- * 1:1; the rest of the GenAI attributes ride along as observation
17
- * metadata.
12
+ * The root {@link TraceSpan} becomes a Langfuse trace AND its top
13
+ * observation (so the root execution's own tokens are metered, not just
14
+ * its children's); every node is an observation — a `generation` when it
15
+ * metered its OWN tokens (LLM-backed agents, supervisors), otherwise a
16
+ * plain `span` (tools, callbacks, and composite nodes whose tokens came
17
+ * only from children). Timing, status, and version map 1:1; each
18
+ * generation reports its own token usage (rolled-up minus children) so
19
+ * the trace total isn't double-counted; non-usage GenAI attributes ride
20
+ * along as metadata, and captured content (under `captureContent`)
21
+ * surfaces as native `input` / `output`.
18
22
  *
19
23
  * @example
20
24
  * collector.use(langfuseExporter({ publicKey: "pk-...", secretKey: "sk-..." }));
@@ -1 +1 @@
1
- {"version":3,"file":"langfuse-exporter.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/langfuse/langfuse-exporter.ts"],"mappings":";;;;;;;AA+EA;;;;;;;;AAAoF;;;;;;;;iBAApE,gBAAA,CAAiB,OAAA,EAAS,uBAAA,GAA0B,gBAAgB"}
1
+ {"version":3,"file":"langfuse-exporter.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/ai-panoptic/src/exporters/langfuse/langfuse-exporter.ts"],"mappings":";;;;;;;AAoFA;;;;;;;;AAAoF;;;;;;;;;;;;iBAApE,gBAAA,CAAiB,OAAA,EAAS,uBAAA,GAA0B,gBAAgB"}