@yagni-app/code 1.0.6 → 1.0.7

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 (85) hide show
  1. package/README.md +84 -46
  2. package/dist/claudeCompat.d.ts +59 -0
  3. package/dist/claudeCompat.js +109 -2
  4. package/dist/claudePlugins.d.ts +45 -5
  5. package/dist/claudePlugins.js +129 -21
  6. package/dist/cli.js +16 -4
  7. package/dist/doctor.d.ts +21 -8
  8. package/dist/doctor.js +53 -28
  9. package/dist/extension/condensedTools.d.ts +12 -1
  10. package/dist/extension/condensedTools.js +17 -9
  11. package/dist/extension/index.d.ts +13 -0
  12. package/dist/extension/index.js +230 -43
  13. package/dist/extension/mcp/cliConfig.d.ts +1 -1
  14. package/dist/extension/mcp/cliConfig.js +1 -1
  15. package/dist/extension/mcp/config.d.ts +24 -2
  16. package/dist/extension/mcp/config.js +75 -3
  17. package/dist/extension/mcp/manager.d.ts +3 -1
  18. package/dist/extension/mcp/manager.js +2 -2
  19. package/dist/extension/mcp/panel.d.ts +0 -1
  20. package/dist/extension/mcp/panel.js +13 -3
  21. package/dist/extension/mcp/startup.js +8 -6
  22. package/dist/extension/permission/gate.d.ts +97 -2
  23. package/dist/extension/permission/gate.js +375 -26
  24. package/dist/extension/permissionRules/bashFileArgs.d.ts +39 -0
  25. package/dist/extension/permissionRules/bashFileArgs.js +236 -0
  26. package/dist/extension/permissionRules/engine.d.ts +50 -0
  27. package/dist/extension/permissionRules/engine.js +238 -0
  28. package/dist/extension/permissionRules/loadConfig.d.ts +53 -0
  29. package/dist/extension/permissionRules/loadConfig.js +90 -0
  30. package/dist/extension/permissionRules/parser.d.ts +38 -0
  31. package/dist/extension/permissionRules/parser.js +136 -0
  32. package/dist/extension/permissionRules/pathRules.d.ts +58 -0
  33. package/dist/extension/permissionRules/pathRules.js +120 -0
  34. package/dist/extension/permissionRules/shellRules.d.ts +52 -0
  35. package/dist/extension/permissionRules/shellRules.js +221 -0
  36. package/dist/extension/pipeline/invocation.d.ts +3 -6
  37. package/dist/extension/pipeline/invocation.js +3 -6
  38. package/dist/extension/pipeline/runner.d.ts +0 -1
  39. package/dist/extension/pipeline/runner.js +6 -14
  40. package/dist/extension/plugins/inventory.d.ts +88 -0
  41. package/dist/extension/plugins/inventory.js +144 -0
  42. package/dist/extension/plugins/panel.d.ts +45 -0
  43. package/dist/extension/plugins/panel.js +293 -0
  44. package/dist/extension/sandbox/bash.d.ts +99 -0
  45. package/dist/extension/sandbox/bash.js +190 -0
  46. package/dist/extension/sandbox/config.d.ts +114 -0
  47. package/dist/extension/sandbox/config.js +366 -0
  48. package/dist/extension/sandbox/manager.d.ts +98 -0
  49. package/dist/extension/sandbox/manager.js +216 -0
  50. package/dist/extension/sandbox/panel.d.ts +111 -0
  51. package/dist/extension/sandbox/panel.js +342 -0
  52. package/dist/extension/sandbox/session.d.ts +85 -0
  53. package/dist/extension/sandbox/session.js +775 -0
  54. package/dist/extension/telemetry/attrs.d.ts +96 -0
  55. package/dist/extension/telemetry/attrs.js +149 -0
  56. package/dist/extension/telemetry/config.d.ts +99 -0
  57. package/dist/extension/telemetry/config.js +193 -0
  58. package/dist/extension/telemetry/index.d.ts +7 -0
  59. package/dist/extension/telemetry/index.js +7 -0
  60. package/dist/extension/telemetry/probe.d.ts +29 -0
  61. package/dist/extension/telemetry/probe.js +122 -0
  62. package/dist/extension/telemetry/register.d.ts +40 -0
  63. package/dist/extension/telemetry/register.js +192 -0
  64. package/dist/extension/telemetry/sdk.d.ts +63 -0
  65. package/dist/extension/telemetry/sdk.js +207 -0
  66. package/dist/extension/telemetry/tracker.d.ts +131 -0
  67. package/dist/extension/telemetry/tracker.js +551 -0
  68. package/dist/extension/vendor/IGNORE-LICENSE-MIT +21 -0
  69. package/dist/extension/vendor/ignore.d.ts +86 -0
  70. package/dist/extension/vendor/ignore.js +788 -0
  71. package/dist/goHeadless.d.ts +1 -1
  72. package/dist/goHeadless.js +2 -2
  73. package/dist/launch.d.ts +4 -3
  74. package/dist/launch.js +7 -4
  75. package/dist/mcpCommand.d.ts +10 -1
  76. package/dist/mcpCommand.js +42 -10
  77. package/dist/otel.d.ts +67 -90
  78. package/dist/otel.js +152 -195
  79. package/dist/paths.d.ts +13 -0
  80. package/dist/paths.js +18 -0
  81. package/dist/pluginCommand.d.ts +43 -0
  82. package/dist/pluginCommand.js +499 -0
  83. package/dist/pluginStore.d.ts +170 -0
  84. package/dist/pluginStore.js +554 -0
  85. package/package.json +19 -3
@@ -65,7 +65,7 @@ export declare function buildHeadlessChildEnv(opts: {
65
65
  workspaceId?: string;
66
66
  cliVersion?: string;
67
67
  sessionId?: string;
68
- /** Resolved OTel export config; stage children load pi-otel when present. */
68
+ /** Resolved OTel export config; stage children export when present. */
69
69
  otel?: OtelLaunchConfig;
70
70
  }): NodeJS.ProcessEnv;
71
71
  /**
@@ -55,8 +55,8 @@ export function buildHeadlessChildEnv(opts) {
55
55
  PI_CODING_AGENT_DIR: piAgentDir,
56
56
  PI_SKIP_VERSION_CHECK: "1",
57
57
  PI_TELEMETRY: "0",
58
- // OTel export: pin metadata-only capture and forward pi-otel's path so
59
- // every stage child spawns with it (see otel.ts for the policy).
58
+ // OTel export: the gate plus the OTLP env, inherited by every stage child
59
+ // (see otel.ts for the policy).
60
60
  ...(opts.otel ? otelChildEnv(opts.otel, opts.baseEnv) : {}),
61
61
  };
62
62
  }
package/dist/launch.d.ts CHANGED
@@ -95,9 +95,10 @@ export interface BuildLaunchOptions {
95
95
  extraEnv?: Record<string, string>;
96
96
  /**
97
97
  * Resolved OTel export config (see `otel.ts`). When present, the session
98
- * loads pi-otel alongside our extension and the env pins metadata-only
99
- * capture; absent means no OTLP endpoint is configured and the launch is
100
- * byte-for-byte what it was before OTel support existed.
98
+ * env carries the export gate and the OTLP variables and the bundled
99
+ * extension exports traces, metrics, and log events; absent means no OTLP
100
+ * endpoint is configured and the launch is byte-for-byte what it was
101
+ * before OTel support existed.
101
102
  */
102
103
  otel?: OtelLaunchConfig;
103
104
  }
package/dist/launch.js CHANGED
@@ -9,6 +9,7 @@ import { randomUUID } from "node:crypto";
9
9
  import { agentDirEnvVar } from "./branding.js";
10
10
  import { otelChildEnv } from "./otel.js";
11
11
  import { PAD_X_ENV, resolvePadX } from "./padding.js";
12
+ import { resolveCliPath } from "./paths.js";
12
13
  import { ENGINEERING_PRACTICE_SECTION, promptEnrichmentDisabled } from "./promptEnrichment.js";
13
14
  /**
14
15
  * How close to expiry the token can be before launch warns. A coding session
@@ -76,6 +77,9 @@ export function buildLaunch(creds, passthroughArgs, opts) {
76
77
  // the launcher read (0600). Absent when the caller can't resolve it.
77
78
  ...(opts.profilePath ? { YAGNI_PROFILE_PATH: opts.profilePath } : {}),
78
79
  ...(opts.stateDir ? { YAGNI_CODE_HOME: opts.stateDir } : {}),
80
+ // The launcher's own entry, so the in-session /plugin panel and
81
+ // /reload-plugins can run `yagni plugin …` with the same binary.
82
+ YAGNI_CLI_PATH: resolveCliPath(),
79
83
  // Forward the launcher's version so the extension's crash reports carry
80
84
  // the shipping version (the bundled extension has no package.json to read).
81
85
  ...(opts.cliVersion ? { YAGNI_CODE_VERSION: opts.cliVersion } : {}),
@@ -106,9 +110,9 @@ export function buildLaunch(creds, passthroughArgs, opts) {
106
110
  // the editor (which the launcher pads by seeding editorPaddingX). The
107
111
  // footer can't read pi's settings, so the value crosses over env.
108
112
  [PAD_X_ENV]: String(resolvePadX()),
109
- // OTel export (gated in otel.ts): pin metadata-only capture and forward
110
- // pi-otel's path so /go stage children and subagents load it too. Placed
111
- // after baseEnv on purpose the capture pin must beat a user env override.
113
+ // OTel export (gated in otel.ts): the YAGNI_OTEL_EXPORT gate plus the
114
+ // workspace's OTLP env; the bundled extension does the exporting, and
115
+ // /go stage children and subagents inherit this env so they export too.
112
116
  ...(opts.otel ? otelChildEnv(opts.otel, opts.baseEnv ?? {}) : {}),
113
117
  };
114
118
  // Always load our extension. Default the provider to `yagni` unless the user
@@ -130,7 +134,6 @@ export function buildLaunch(creds, passthroughArgs, opts) {
130
134
  const argv = [
131
135
  "-e",
132
136
  opts.extensionPath,
133
- ...(opts.otel ? ["-e", opts.otel.extensionPath] : []),
134
137
  ...(userChoseProvider ? [] : ["--provider", "yagni"]),
135
138
  ...(userChoseModel ? [] : ["--model", "advanced"]),
136
139
  ...(enrichmentOff ? [] : ["--append-system-prompt", ENGINEERING_PRACTICE_SECTION]),
@@ -22,6 +22,12 @@ export interface McpDeps {
22
22
  stderr?: (text: string) => void;
23
23
  /** Test seam: the extension module to import (defaults to the built copy). */
24
24
  loadMcpModule?: () => Promise<McpCliModule>;
25
+ /**
26
+ * Test seam: the plugin MCP payload the launcher would hand a session
27
+ * (defaults to running the real, non-interactive discovery). Lets `list`
28
+ * and `get` show plugin-bundled servers outside a session.
29
+ */
30
+ pluginMcpEnv?: () => Promise<string | undefined>;
25
31
  /** Test seam: health probe override (defaults to mod.probeServer). */
26
32
  probeServer?: (name: string, config: unknown, opts?: {
27
33
  env?: NodeJS.ProcessEnv;
@@ -73,12 +79,15 @@ export interface McpCliModule {
73
79
  missingVars: string[];
74
80
  };
75
81
  }
82
+ export type McpListScope = "user" | "project" | "local" | "plugin";
76
83
  export interface McpLoadResult {
77
84
  servers: {
78
85
  name: string;
79
- scope: "user" | "project" | "local";
86
+ scope: McpListScope;
80
87
  config: Record<string, unknown>;
81
88
  sourcePath: string;
89
+ plugin?: string;
90
+ gated?: boolean;
82
91
  }[];
83
92
  errors: {
84
93
  sourcePath: string;
@@ -18,7 +18,10 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
18
18
  import { homedir } from "node:os";
19
19
  import { join } from "node:path";
20
20
  import { fileURLToPath } from "node:url";
21
+ import { CLAUDE_PLUGIN_MCP_ENV, claudeCompatArgs } from "./claudeCompat.js";
22
+ import { agentDir } from "./credentials.js";
21
23
  import { DISTRIBUTION } from "./distribution.js";
24
+ import { getActiveProfileName } from "./profiles.js";
22
25
  export function resolveMcpConfigPath() {
23
26
  const bundled = fileURLToPath(new URL("./extension/mcp/cliConfig.js", import.meta.url));
24
27
  if (existsSync(bundled))
@@ -229,6 +232,25 @@ function describeScopePath(scope, cwd) {
229
232
  return join(cwd, ".mcp.json");
230
233
  return "~/.yagni-code/mcp.json";
231
234
  }
235
+ /** The plugin MCP payload for `cwd`, computed the way a launch would (trust respected, never prompting). */
236
+ async function defaultPluginMcpEnv(cwd) {
237
+ try {
238
+ const profile = await getActiveProfileName();
239
+ const compat = await claudeCompatArgs({ cwd, agentDir: agentDir(profile), interactive: false });
240
+ return compat.env[CLAUDE_PLUGIN_MCP_ENV];
241
+ }
242
+ catch {
243
+ return undefined;
244
+ }
245
+ }
246
+ /** `list`/`get` see plugin servers even outside a session: merge the payload into the env they load with. */
247
+ async function envWithPluginMcp(deps, cwd) {
248
+ const base = deps.env ?? process.env;
249
+ if (base[CLAUDE_PLUGIN_MCP_ENV])
250
+ return base;
251
+ const payload = await (deps.pluginMcpEnv ?? defaultPluginMcpEnv)(cwd);
252
+ return payload ? { ...base, [CLAUDE_PLUGIN_MCP_ENV]: payload } : base;
253
+ }
232
254
  export async function mcpCommand(args, deps = {}) {
233
255
  const stdout = deps.stdout ?? ((t) => process.stdout.write(t));
234
256
  const stderr = deps.stderr ?? ((t) => process.stderr.write(t));
@@ -261,9 +283,9 @@ export async function mcpCommand(args, deps = {}) {
261
283
  case "remove":
262
284
  return mcpRemove(mod, parsed, { cwd, stdout, stderr });
263
285
  case "list":
264
- return mcpList(mod, { cwd, stdout, stderr, env: deps.env ?? process.env, probeServer: deps.probeServer });
286
+ return mcpList(mod, { cwd, stdout, stderr, env: await envWithPluginMcp(deps, cwd), probeServer: deps.probeServer });
265
287
  case "get":
266
- return mcpGet(mod, parsed, { cwd, stdout, stderr, env: deps.env ?? process.env, probeServer: deps.probeServer });
288
+ return mcpGet(mod, parsed, { cwd, stdout, stderr, env: await envWithPluginMcp(deps, cwd), probeServer: deps.probeServer });
267
289
  case "reset-project-choices":
268
290
  return mcpResetChoices(mod, { cwd, stdout, stderr });
269
291
  case "add-from-claude":
@@ -548,6 +570,8 @@ async function mcpGet(mod, parsed, io) {
548
570
  }
549
571
  io.stdout(`${name}:\n`);
550
572
  io.stdout(` Scope: ${scopeLabel(server.scope)}\n`);
573
+ if (server.plugin)
574
+ io.stdout(` Plugin: ${server.plugin}\n`);
551
575
  const config = server.config;
552
576
  if (config["type"] === "http" || config["type"] === "sse") {
553
577
  io.stdout(` Type: ${config["type"]}\n`);
@@ -567,14 +591,19 @@ async function mcpGet(mod, parsed, io) {
567
591
  io.stdout(` Env: ${key}=${value}\n`);
568
592
  }
569
593
  }
570
- if (server.scope === "project") {
594
+ if (server.scope === "project" || server.gated) {
571
595
  const { state } = mod.readProjectApproval(mod.resolveProjectRoot(io.cwd));
572
596
  const decision = mod.decisionFor(state, name);
573
597
  io.stdout(` Approved: ${decision === "enabled" ? "yes" : decision === "disabled" ? "no" : "not yet decided"}\n`);
574
598
  }
575
599
  io.stdout(` Config file: ${server.sourcePath}\n`);
576
600
  io.stdout(await healthLine(mod, server, io));
577
- io.stdout(`\nTo remove this server, run: ${DISTRIBUTION.commandName} mcp remove "${name}"\n`);
601
+ if (server.scope === "plugin") {
602
+ io.stdout(`\nThis server is bundled with plugin ${server.plugin}; remove it with: ${DISTRIBUTION.commandName} plugin uninstall "${server.plugin}"\n`);
603
+ }
604
+ else {
605
+ io.stdout(`\nTo remove this server, run: ${DISTRIBUTION.commandName} mcp remove "${name}"\n`);
606
+ }
578
607
  return 0;
579
608
  }
580
609
  function printOAuthDetail(mod, name, config, stdout) {
@@ -590,7 +619,7 @@ function printOAuthDetail(mod, name, config, stdout) {
590
619
  async function healthLine(mod, server, io) {
591
620
  // Approval gate: an undecided/disabled project server is reported WITHOUT
592
621
  // connecting (fail-closed — never spawn an unapproved process).
593
- if (server.scope === "project") {
622
+ if (server.scope === "project" || server.gated) {
594
623
  const { state } = mod.readProjectApproval(mod.resolveProjectRoot(io.cwd));
595
624
  const decision = mod.decisionFor(state, server.name);
596
625
  if (decision === "disabled")
@@ -613,17 +642,18 @@ async function mcpList(mod, io) {
613
642
  return 0;
614
643
  }
615
644
  const lines = [];
616
- const byScope = { user: [], project: [], local: [] };
645
+ const byScope = { user: [], project: [], local: [], plugin: [] };
617
646
  for (const s of servers)
618
647
  (byScope[s.scope] ??= []).push(s);
619
- for (const scope of ["local", "project", "user"]) {
648
+ const labels = { local: "Local", project: "Project", user: "User", plugin: "Plugin" };
649
+ for (const scope of ["local", "project", "user", "plugin"]) {
620
650
  const group = byScope[scope];
621
651
  if (!group?.length)
622
652
  continue;
623
- lines.push(`${scope === "local" ? "Local" : scope === "project" ? "Project" : "User"} servers:`);
653
+ lines.push(`${labels[scope]} servers:`);
624
654
  for (const s of group) {
625
655
  const status = await listHealthLine(mod, s, io);
626
- lines.push(` ${s.name} · ${status}`);
656
+ lines.push(` ${s.name}${s.plugin ? ` (${s.plugin})` : ""} · ${status}`);
627
657
  }
628
658
  }
629
659
  for (const err of errors)
@@ -634,7 +664,7 @@ async function mcpList(mod, io) {
634
664
  return 0;
635
665
  }
636
666
  async function listHealthLine(mod, server, io) {
637
- if (server.scope === "project") {
667
+ if (server.scope === "project" || server.gated) {
638
668
  const { state } = mod.readProjectApproval(mod.resolveProjectRoot(io.cwd));
639
669
  const decision = mod.decisionFor(state, server.name);
640
670
  if (decision === "disabled")
@@ -654,6 +684,8 @@ function scopeLabel(scope) {
654
684
  return "Local config (private to you in this project)";
655
685
  if (scope === "project")
656
686
  return "Project config (shared via .mcp.json)";
687
+ if (scope === "plugin")
688
+ return "Plugin (bundled with an installed plugin)";
657
689
  return "User config (available in all your projects)";
658
690
  }
659
691
  function mcpResetChoices(mod, io) {
package/dist/otel.d.ts CHANGED
@@ -1,69 +1,55 @@
1
1
  /**
2
- * OTel export wiring (Updater pilot ask): bake the `pi-otel` extension into
3
- * every launch so a workspace can stream session tracestoken usage, cost,
4
- * model tier, tool calls — to its own OTLP collector (e.g. Datadog) without
5
- * anyone hand-installing an extension.
2
+ * OTel export gate (Updater pilot ask, now Claude Code parity): decide
3
+ * whether a launch streams session telemetrytraces, metrics, and log
4
+ * events — to the workspace's own OTLP collector (Datadog, Grafana, an
5
+ * in-house collector), and hand the session the standard OpenTelemetry env
6
+ * it needs. The exporter itself lives in the bundled extension
7
+ * (`pi-extension-yagni/src/telemetry`), so there is nothing extra to load and
8
+ * nothing that can go missing on a customer's `npm install`.
6
9
  *
7
- * Three deliberate policies, all enforced here rather than left to defaults:
10
+ * Three deliberate policies, enforced here rather than left to defaults:
8
11
  *
9
- * 1. GATED, not always-on. pi-otel ships `enabled: true` pointed at
10
- * `localhost:4317`, so loading it unconditionally would have every session
11
- * probing a collector nobody runs. We only pass the extension to pi when an
12
- * OTLP endpoint is actually configured: `OTEL_EXPORTER_OTLP_ENDPOINT` in the
13
- * environment, or `otel.endpoint` in the repo's committed `.pi/settings.json`
14
- * (the file pi-otel itself reads, so teams can configure once per repo).
12
+ * 1. GATED, not always-on. Nothing exports unless an OTLP destination is
13
+ * configured: the user's own OTEL_* env, the workspace's admin-set config
14
+ * (fetched fail-soft and cached), or the repo's committed
15
+ * `.pi/settings.json`. The gate result travels to the session as
16
+ * `YAGNI_OTEL_EXPORT=1` plus the OTLP variables; children (/go stages,
17
+ * subagents) inherit the env and export too.
15
18
  *
16
- * 2. METADATA-ONLY, enforced. Cost, tokens, model, finish reasons and tool-call
17
- * ids export; prompt and response text never do. `PI_OTEL_CAPTURE_CONTENT`
18
- * is pinned in the child env, and env beats settings in pi-otel's own
19
- * precedence — a repo settings file asking for "full" is overridden, not
20
- * honored. Session content leaving the machine is a contract change, not a
21
- * config knob.
19
+ * 2. METADATA-ONLY, in code. Token counts, cost, tier, durations, tool names,
20
+ * and sizes export; prompt, response, and tool content never do. The
21
+ * extension enforces this without a knob no env var re-enables content.
22
22
  *
23
- * 3. Cost is the customer's SELL rate. pi-otel exports pi's `usage.cost.total`
24
- * verbatim (as `pi.cost.usd`), and pi computes that from the catalog's
25
- * tier rates (YAG-381) — so the collector sees contracted prices, and the
26
- * exported model name is the opaque tier id, never the backing model.
23
+ * 3. Cost is the customer's SELL rate. Usage cost is pi's `usage.cost.total`,
24
+ * computed from the catalog's tier rates (YAG-381), and the exported model
25
+ * name is the opaque tier id, never the backing model.
27
26
  *
28
- * Everything here is fail-soft: telemetry must never block a launch, so a
29
- * missing package or unreadable settings file resolves to "disabled".
27
+ * Everything here is fail-soft: telemetry must never block a launch, so an
28
+ * unreadable settings file or an offline backend resolves to "disabled" or
29
+ * to last-known config.
30
30
  */
31
31
  import { readFileSync } from "node:fs";
32
- /**
33
- * Env var carrying the resolved pi-otel entry path to /go stage children and
34
- * subagents: they build their own pi argv inside pi-extension-yagni (which
35
- * cannot import this package), so the path crosses over env. The extension
36
- * reads the same literal in `pipeline/runner.ts`.
37
- */
38
- export declare const OTEL_EXTENSION_PATH_ENV = "YAGNI_OTEL_EXTENSION_PATH";
32
+ /** The launcher's "this session exports" gate, read by the bundled extension. */
33
+ export declare const OTEL_EXPORT_ENV = "YAGNI_OTEL_EXPORT";
39
34
  /** The service name a collector sees unless the user set their own. */
40
35
  export declare const DEFAULT_OTEL_SERVICE_NAME = "yagni-code";
36
+ /** The standard per-signal endpoint variables, generic first (its precedence). */
37
+ declare const ENDPOINT_ENV_VARS: readonly ["OTEL_EXPORTER_OTLP_ENDPOINT", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"];
38
+ export type EndpointEnvVar = (typeof ENDPOINT_ENV_VARS)[number];
41
39
  export interface OtelLaunchConfig {
42
- /** Absolute path to pi-otel's extension entry. */
43
- extensionPath: string;
44
40
  /**
45
- * Where the enablement signal came from. "workspace" additionally carries
46
- * protocol/headers/serviceName, delivered as env to the session; "env" may
47
- * carry a protocol when the endpoint arrived via the per-signal
48
- * `OTEL_EXPORTER_OTLP_TRACES_(ENDPOINT|PROTOCOL)` bridge (pi-otel reads only
49
- * the generic var, so the child env translates); "project-settings" leaves
50
- * those to whatever the user/repo already configured.
41
+ * Where the enablement signal came from. "workspace" and "project-settings"
42
+ * carry endpoint/protocol/headers/serviceName that the launcher delivers as
43
+ * env; "env" is the user's own setup, passed through untouched.
51
44
  */
52
45
  source: "env" | "workspace" | "project-settings";
53
- /**
54
- * The env var the endpoint actually came from — the generic
55
- * `OTEL_EXPORTER_OTLP_ENDPOINT` or the per-signal
56
- * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`. Doctor surfaces it so a user who set
57
- * the per-signal var sees their own var named, not the generic one.
58
- */
59
- envVar?: "OTEL_EXPORTER_OTLP_ENDPOINT" | "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
60
- /** The configured OTLP endpoint (for doctor display; env for workspace source). */
46
+ /** The env var the endpoint came from (env source only; doctor names it). */
47
+ envVar?: EndpointEnvVar;
48
+ /** The configured endpoint: a BASE URL for workspace/project sources. */
61
49
  endpoint: string;
62
- /** OTLP protocol: workspace config, or a per-signal env bridge. */
63
50
  protocol?: string;
64
- /** Workspace-configured collector headers (workspace source only; carries secrets). */
51
+ /** Collector headers (workspace / project sources; carries secrets). */
65
52
  headers?: Record<string, string>;
66
- /** Workspace-configured service name (workspace source only). */
67
53
  serviceName?: string;
68
54
  }
69
55
  /**
@@ -79,51 +65,44 @@ export interface WorkspaceOtelConfig {
79
65
  headers?: Record<string, string>;
80
66
  }
81
67
  /**
82
- * Decide whether this launch exports OTel traces, and with which pi-otel entry.
83
- * Returns undefined when export stays off: no endpoint configured anywhere,
84
- * `PI_OTEL_DISABLED` set, or the pi-otel package unresolvable (never fatal).
68
+ * Reduce an endpoint that carries a signal path to its base
69
+ * (`https://otlp.datadoghq.com/v1/traces` `https://otlp.datadoghq.com`).
70
+ * The extension appends `/v1/<signal>` per signal for http transports, so a
71
+ * workspace config saved before logs and metrics existed keeps exporting all
72
+ * three without an admin re-save. Mirrors the extension's own helper.
73
+ */
74
+ export declare function stripSignalPath(endpoint: string): string;
75
+ /** Personal kill switch: beats every source. Both spellings accepted. */
76
+ export declare function envDisabled(env: NodeJS.ProcessEnv): boolean;
77
+ /**
78
+ * Decide whether this launch exports, from the user's env and the repo's
79
+ * settings. Returns undefined when export stays off: no endpoint configured,
80
+ * or a kill switch set.
85
81
  *
86
- * Env sources, in precedence order: the generic `OTEL_EXPORTER_OTLP_ENDPOINT`,
87
- * then the per-signal `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`. pi-otel only reads
88
- * the generic var, so the per-signal one (the standard OTel env shape, and
89
- * what Claude Code setups configure — e.g. Updater's) is bridged: the traces
90
- * endpoint becomes the generic one, and a per-signal TRACES_PROTOCOL carries
91
- * over the same way. Logs/metrics per-signal vars are deliberately NOT read:
92
- * sessions export traces only, and pi-otel's metrics/logs signals are opt-in
93
- * elsewhere.
82
+ * Any of the standard endpoint variables enables export — the generic
83
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` or a per-signal one so a machine that
84
+ * carries a Claude Code telemetry env (typically all three per-signal
85
+ * endpoints) lights up with no translation. The extension reads the same
86
+ * variables, per signal, following the OTLP exporter spec.
94
87
  */
95
88
  export declare function resolveOtelLaunch(opts: {
96
89
  env: NodeJS.ProcessEnv;
97
90
  cwd: string;
98
- /** Injectable seams for tests. */
99
- resolveExtension?: () => string;
91
+ /** Injectable seam for tests. */
100
92
  readFile?: typeof readFileSync;
101
93
  }): OtelLaunchConfig | undefined;
102
- /** Absolute path to pi-otel's extension entry (its package main IS the pi
103
- * extension entry, `dist/index.js`). Resolved ESM-native like `paths.ts` does
104
- * for pi itself. Throws when the package is missing — callers treat that as
105
- * "export off", not an error.
106
- */
107
- export declare function resolveOtelExtensionPath(): string;
108
- /**
109
- * TCP reachability probe for an OTLP endpoint URL, with scheme-default ports:
110
- * WHATWG `URL` strips `:443`/`:80` as defaults, so an https endpoint with no
111
- * port probes 443 (the agentless SaaS shape). Returns false on any parse or
112
- * connect failure — reachability is advisory, never fatal. Mirrors the probe
113
- * inside (patched) pi-otel but lives here so `doctor` can use it without
114
- * importing the extension's SDK graph.
115
- */
116
- export declare function probeOtelEndpoint(endpoint: string, timeoutMs?: number): Promise<boolean>;
117
94
  /**
118
- * The env keys an OTel-exporting child must carry. `PI_OTEL_CAPTURE_CONTENT`
119
- * is pinned unconditionally (policy 2 above); the service name only fills in
120
- * when the user has not chosen their own.
95
+ * The env keys an exporting child must carry. `YAGNI_OTEL_EXPORT=1` is the
96
+ * gate the extension reads; the service name only fills in when the user has
97
+ * not chosen their own.
121
98
  *
122
- * A "workspace"-sourced config additionally delivers the admin-set endpoint,
123
- * protocol, and collector headers over the standard OTel env vars — but any
124
- * of those the USER already set in their own environment wins (local env >
125
- * workspace config), so an engineer can point one session at a scratch
126
- * collector without an admin change.
99
+ * A "workspace" or "project-settings" config delivers its endpoint (as the
100
+ * generic BASE endpoint the extension derives the per-signal URLs),
101
+ * protocol, headers, and service name over the standard OTel env vars but
102
+ * any of those the USER already set in their own environment wins (local
103
+ * env > workspace config), so an engineer can point one session at a
104
+ * scratch collector without an admin change. An "env" config is the user's
105
+ * own setup and passes through untouched.
127
106
  */
128
107
  export declare function otelChildEnv(config: OtelLaunchConfig, baseEnv: NodeJS.ProcessEnv): Record<string, string>;
129
108
  export interface WorkspaceOtelFetchDeps {
@@ -154,10 +133,8 @@ export declare function fetchWorkspaceOtel(creds: {
154
133
  /**
155
134
  * Full launch-time resolution, all four sources in precedence order:
156
135
  *
157
- * 1. `PI_OTEL_DISABLED` — personal kill switch, beats everything.
158
- * 2. env `OTEL_EXPORTER_OTLP_ENDPOINT` / `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`
159
- * — the user's own setup, untouched (generic wins
160
- * over per-signal).
136
+ * 1. `PI_OTEL_DISABLED` / `YAGNI_OTEL_DISABLED` — personal kill switch.
137
+ * 2. env `OTEL_EXPORTER_OTLP_*_ENDPOINT` the user's own setup, untouched.
161
138
  * 3. workspace config — admin-set in the web app, fetched/cached.
162
139
  * 4. repo `.pi/settings.json` — committed per-repo config.
163
140
  *
@@ -171,9 +148,9 @@ export declare function resolveOtelLaunchWithWorkspace(opts: {
171
148
  token: string;
172
149
  } | null;
173
150
  profileName?: string;
174
- resolveExtension?: () => string;
175
151
  readFile?: typeof readFileSync;
176
152
  fetchDeps?: WorkspaceOtelFetchDeps;
177
153
  fetchWorkspace?: typeof fetchWorkspaceOtel;
178
154
  }): Promise<OtelLaunchConfig | undefined>;
155
+ export {};
179
156
  //# sourceMappingURL=otel.d.ts.map