@infinitedusky/indusk-mcp 1.18.0 → 1.18.2

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.
@@ -19,14 +19,21 @@ export interface EvalOtelConfig {
19
19
  }
20
20
  /**
21
21
  * Pure predicate — reads `.indusk/config.json` `eval.otel.{enabled,dataset}` and
22
- * the `INDUSK_EVAL_OTEL` / `INDUSK_EVAL_OTEL_DATASET` / `OTEL_EXPORTER_OTLP_ENDPOINT`
23
- * env vars. Does not init anything or touch the network.
22
+ * the `INDUSK_EVAL_OTEL` / `INDUSK_EVAL_OTEL_DATASET` / `EVAL_AGENT_DATASET` /
23
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` env vars. Does not init anything or touch the network.
24
24
  *
25
25
  * Resolution:
26
26
  * - `enabled`: `INDUSK_EVAL_OTEL=1` (truthy) wins, else config `eval.otel.enabled`, else false.
27
27
  * - `endpoint`: `OTEL_EXPORTER_OTLP_ENDPOINT` (null if unset).
28
- * - `dataset`: `INDUSK_EVAL_OTEL_DATASET` env var wins, else config `eval.otel.dataset`,
29
- * else `"agent"` default. Sent as the `Dash0-Dataset` header on every OTLP export.
28
+ * - `dataset` (priority, highest lowest):
29
+ * 1. `INDUSK_EVAL_OTEL_DATASET` env var (explicit per-invocation override)
30
+ * 2. `EVAL_AGENT_DATASET` env var (composable.env convention — see env/components/dash0.env)
31
+ * 3. `.indusk/config.json` `eval.otel.dataset`
32
+ * 4. `"agent"` default
33
+ *
34
+ * Sent as the `Dash0-Dataset` header on every OTLP export. Also rewritten into
35
+ * `OTEL_EXPORTER_OTLP_HEADERS` if present there (env headers beat constructor
36
+ * headers per OTel spec — so we fix the env header at the source).
30
37
  */
31
38
  export declare function isEvalOtelEnabled(projectRoot: string): EvalOtelConfig;
32
39
  /**
@@ -34,19 +34,27 @@ function syslog(projectRoot, msg) {
34
34
  const DEFAULT_DATASET = "agent";
35
35
  /**
36
36
  * Pure predicate — reads `.indusk/config.json` `eval.otel.{enabled,dataset}` and
37
- * the `INDUSK_EVAL_OTEL` / `INDUSK_EVAL_OTEL_DATASET` / `OTEL_EXPORTER_OTLP_ENDPOINT`
38
- * env vars. Does not init anything or touch the network.
37
+ * the `INDUSK_EVAL_OTEL` / `INDUSK_EVAL_OTEL_DATASET` / `EVAL_AGENT_DATASET` /
38
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` env vars. Does not init anything or touch the network.
39
39
  *
40
40
  * Resolution:
41
41
  * - `enabled`: `INDUSK_EVAL_OTEL=1` (truthy) wins, else config `eval.otel.enabled`, else false.
42
42
  * - `endpoint`: `OTEL_EXPORTER_OTLP_ENDPOINT` (null if unset).
43
- * - `dataset`: `INDUSK_EVAL_OTEL_DATASET` env var wins, else config `eval.otel.dataset`,
44
- * else `"agent"` default. Sent as the `Dash0-Dataset` header on every OTLP export.
43
+ * - `dataset` (priority, highest lowest):
44
+ * 1. `INDUSK_EVAL_OTEL_DATASET` env var (explicit per-invocation override)
45
+ * 2. `EVAL_AGENT_DATASET` env var (composable.env convention — see env/components/dash0.env)
46
+ * 3. `.indusk/config.json` `eval.otel.dataset`
47
+ * 4. `"agent"` default
48
+ *
49
+ * Sent as the `Dash0-Dataset` header on every OTLP export. Also rewritten into
50
+ * `OTEL_EXPORTER_OTLP_HEADERS` if present there (env headers beat constructor
51
+ * headers per OTel spec — so we fix the env header at the source).
45
52
  */
46
53
  export function isEvalOtelEnabled(projectRoot) {
47
54
  const envFlag = process.env.INDUSK_EVAL_OTEL;
48
55
  const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? null;
49
- const envDataset = process.env.INDUSK_EVAL_OTEL_DATASET;
56
+ const explicitDataset = process.env.INDUSK_EVAL_OTEL_DATASET;
57
+ const composableDataset = process.env.EVAL_AGENT_DATASET;
50
58
  let configEnabled = false;
51
59
  let configDataset;
52
60
  const configPath = join(projectRoot, ".indusk", "config.json");
@@ -63,13 +71,31 @@ export function isEvalOtelEnabled(projectRoot) {
63
71
  }
64
72
  }
65
73
  const envForcesEnabled = envFlag !== undefined && envFlag !== "" && envFlag !== "0" && envFlag.toLowerCase() !== "false";
66
- const dataset = envDataset && envDataset !== "" ? envDataset : (configDataset ?? DEFAULT_DATASET);
74
+ const dataset = (explicitDataset && explicitDataset !== "" && explicitDataset) ||
75
+ (composableDataset && composableDataset !== "" && composableDataset) ||
76
+ configDataset ||
77
+ DEFAULT_DATASET;
67
78
  return {
68
79
  enabled: envForcesEnabled || configEnabled,
69
80
  endpoint,
70
81
  dataset,
71
82
  };
72
83
  }
84
+ /**
85
+ * Rewrite the `Dash0-Dataset=<old>` entry in `OTEL_EXPORTER_OTLP_HEADERS` to
86
+ * `Dash0-Dataset=<target>`. OTel spec says env-set headers override constructor
87
+ * headers, so we have to fix the env directly for routing to work when the user's
88
+ * shell already sets `OTEL_EXPORTER_OTLP_HEADERS` via composable.env.
89
+ *
90
+ * No-op if the env var is unset or doesn't contain `Dash0-Dataset=`.
91
+ */
92
+ function rewriteDatasetInEnvHeaders(target) {
93
+ const current = process.env.OTEL_EXPORTER_OTLP_HEADERS;
94
+ if (!current || !current.includes("Dash0-Dataset="))
95
+ return;
96
+ const rewritten = current.replace(/Dash0-Dataset=[^,]*/g, `Dash0-Dataset=${target}`);
97
+ process.env.OTEL_EXPORTER_OTLP_HEADERS = rewritten;
98
+ }
73
99
  let activeProvider = null;
74
100
  /**
75
101
  * Initialize OTel tracing for the evaluator if enabled + endpoint set.
@@ -92,16 +118,34 @@ export function initEvalOtel(projectRoot) {
92
118
  if (activeProvider) {
93
119
  return trace.getTracer(TRACER_NAME);
94
120
  }
121
+ // Ensure env-set OTEL_EXPORTER_OTLP_HEADERS routes to the eval agent's
122
+ // dataset. Env headers beat constructor headers per OTel spec — so if the
123
+ // user's shell (composable.env) already set Dash0-Dataset for project
124
+ // telemetry, we rewrite it in-place to the eval agent dataset before the
125
+ // exporter reads it.
126
+ rewriteDatasetInEnvHeaders(dataset);
127
+ // Build exporter headers. We pass Authorization and Dash0-Dataset in the
128
+ // constructor rather than relying on OTEL_EXPORTER_OTLP_HEADERS env parsing,
129
+ // because the OTel SDK's env parser has proven unreliable for tokens with
130
+ // spaces (e.g., "Bearer auth_xxx") in practice — the header silently fails
131
+ // to attach and exports retry-loop to no effect.
132
+ //
133
+ // Precedence:
134
+ // 1. User-set `OTEL_EXPORTER_OTLP_HEADERS` env (handled by SDK, takes top precedence per OTel spec)
135
+ // 2. Explicit constructor headers below (our defaults)
136
+ //
137
+ // DASH0_API_TOKEN is the conventional name we inherit from the Dash0 CLI.
138
+ // If set, we build a Bearer header. If not, we rely on the user's env.
139
+ const headers = {
140
+ "Dash0-Dataset": dataset,
141
+ };
142
+ if (process.env.DASH0_API_TOKEN) {
143
+ headers.Authorization = `Bearer ${process.env.DASH0_API_TOKEN}`;
144
+ }
95
145
  try {
96
146
  const exporter = new OTLPTraceExporter({
97
147
  url: endpoint.endsWith("/v1/traces") ? endpoint : `${endpoint.replace(/\/$/, "")}/v1/traces`,
98
- // Route agent spans to the Dash0 dataset named `dataset`. Default
99
- // is "agent". Env-set headers (OTEL_EXPORTER_OTLP_HEADERS) take
100
- // precedence — per the OTel SDK contract — so a user-provided
101
- // Dash0-Dataset in env overrides this default.
102
- headers: {
103
- "Dash0-Dataset": dataset,
104
- },
148
+ headers,
105
149
  });
106
150
  const provider = new NodeTracerProvider({
107
151
  resource: resourceFromAttributes({
@@ -10,8 +10,8 @@ import { spawn } from "node:child_process";
10
10
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
11
11
  import { dirname, join } from "node:path";
12
12
  import { getProjectGroupId } from "../config.js";
13
- import { ingestScorecard } from "./findings.js";
14
13
  import { readUnprocessedHighlights } from "../highlights/highlights.js";
14
+ import { ingestScorecard } from "./findings.js";
15
15
  import { EvalLogWriter } from "./log-writer.js";
16
16
  import { initEvalOtel, shutdownEvalOtel, withSpan } from "./otel.js";
17
17
  import { buildEvaluatorPrompt } from "./prompt-builder.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infinitedusky/indusk-mcp",
3
- "version": "1.18.0",
3
+ "version": "1.18.2",
4
4
  "description": "InDusk development system — skills, MCP tools, and CLI for structured AI-assisted development",
5
5
  "type": "module",
6
6
  "files": [