@osfactory/har 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -765,14 +765,14 @@ var require_url_state_machine = __commonJS({
765
765
  return url.replace(/\u0009|\u000A|\u000D/g, "");
766
766
  }
767
767
  function shortenPath(url) {
768
- const path48 = url.path;
769
- if (path48.length === 0) {
768
+ const path49 = url.path;
769
+ if (path49.length === 0) {
770
770
  return;
771
771
  }
772
- if (url.scheme === "file" && path48.length === 1 && isNormalizedWindowsDriveLetter(path48[0])) {
772
+ if (url.scheme === "file" && path49.length === 1 && isNormalizedWindowsDriveLetter(path49[0])) {
773
773
  return;
774
774
  }
775
- path48.pop();
775
+ path49.pop();
776
776
  }
777
777
  function includesCredentials(url) {
778
778
  return url.username !== "" || url.password !== "";
@@ -2092,7 +2092,7 @@ var require_lib2 = __commonJS({
2092
2092
  let accum = [];
2093
2093
  let accumBytes = 0;
2094
2094
  let abort = false;
2095
- return new Body.Promise(function(resolve33, reject) {
2095
+ return new Body.Promise(function(resolve34, reject) {
2096
2096
  let resTimeout;
2097
2097
  if (_this4.timeout) {
2098
2098
  resTimeout = setTimeout(function() {
@@ -2126,7 +2126,7 @@ var require_lib2 = __commonJS({
2126
2126
  }
2127
2127
  clearTimeout(resTimeout);
2128
2128
  try {
2129
- resolve33(Buffer.concat(accum, accumBytes));
2129
+ resolve34(Buffer.concat(accum, accumBytes));
2130
2130
  } catch (err) {
2131
2131
  reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
2132
2132
  }
@@ -2801,7 +2801,7 @@ var require_lib2 = __commonJS({
2801
2801
  throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
2802
2802
  }
2803
2803
  Body.Promise = fetch3.Promise;
2804
- return new fetch3.Promise(function(resolve33, reject) {
2804
+ return new fetch3.Promise(function(resolve34, reject) {
2805
2805
  const request = new Request3(url, opts);
2806
2806
  const options = getNodeRequestOptions(request);
2807
2807
  const send = (options.protocol === "https:" ? https : http).request;
@@ -2934,7 +2934,7 @@ var require_lib2 = __commonJS({
2934
2934
  requestOpts.body = void 0;
2935
2935
  requestOpts.headers.delete("content-length");
2936
2936
  }
2937
- resolve33(fetch3(new Request3(locationURL, requestOpts)));
2937
+ resolve34(fetch3(new Request3(locationURL, requestOpts)));
2938
2938
  finalize();
2939
2939
  return;
2940
2940
  }
@@ -2955,7 +2955,7 @@ var require_lib2 = __commonJS({
2955
2955
  const codings = headers.get("Content-Encoding");
2956
2956
  if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
2957
2957
  response = new Response3(body, response_options);
2958
- resolve33(response);
2958
+ resolve34(response);
2959
2959
  return;
2960
2960
  }
2961
2961
  const zlibOptions = {
@@ -2965,7 +2965,7 @@ var require_lib2 = __commonJS({
2965
2965
  if (codings == "gzip" || codings == "x-gzip") {
2966
2966
  body = body.pipe(zlib.createGunzip(zlibOptions));
2967
2967
  response = new Response3(body, response_options);
2968
- resolve33(response);
2968
+ resolve34(response);
2969
2969
  return;
2970
2970
  }
2971
2971
  if (codings == "deflate" || codings == "x-deflate") {
@@ -2977,12 +2977,12 @@ var require_lib2 = __commonJS({
2977
2977
  body = body.pipe(zlib.createInflateRaw());
2978
2978
  }
2979
2979
  response = new Response3(body, response_options);
2980
- resolve33(response);
2980
+ resolve34(response);
2981
2981
  });
2982
2982
  raw.on("end", function() {
2983
2983
  if (!response) {
2984
2984
  response = new Response3(body, response_options);
2985
- resolve33(response);
2985
+ resolve34(response);
2986
2986
  }
2987
2987
  });
2988
2988
  return;
@@ -2990,11 +2990,11 @@ var require_lib2 = __commonJS({
2990
2990
  if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
2991
2991
  body = body.pipe(zlib.createBrotliDecompress());
2992
2992
  response = new Response3(body, response_options);
2993
- resolve33(response);
2993
+ resolve34(response);
2994
2994
  return;
2995
2995
  }
2996
2996
  response = new Response3(body, response_options);
2997
- resolve33(response);
2997
+ resolve34(response);
2998
2998
  });
2999
2999
  writeToStream(req, request);
3000
3000
  });
@@ -6351,14 +6351,14 @@ __export(fileFromPath_exports, {
6351
6351
  fileFromPathSync: () => fileFromPathSync,
6352
6352
  isFile: () => isFile
6353
6353
  });
6354
- function createFileFromPath(path48, { mtimeMs, size }, filenameOrOptions, options = {}) {
6354
+ function createFileFromPath(path49, { mtimeMs, size }, filenameOrOptions, options = {}) {
6355
6355
  let filename;
6356
6356
  if (isPlainObject_default2(filenameOrOptions)) {
6357
6357
  [options, filename] = [filenameOrOptions, void 0];
6358
6358
  } else {
6359
6359
  filename = filenameOrOptions;
6360
6360
  }
6361
- const file = new FileFromPath({ path: path48, size, lastModified: mtimeMs });
6361
+ const file = new FileFromPath({ path: path49, size, lastModified: mtimeMs });
6362
6362
  if (!filename) {
6363
6363
  filename = file.name;
6364
6364
  }
@@ -6367,13 +6367,13 @@ function createFileFromPath(path48, { mtimeMs, size }, filenameOrOptions, option
6367
6367
  lastModified: file.lastModified
6368
6368
  });
6369
6369
  }
6370
- function fileFromPathSync(path48, filenameOrOptions, options = {}) {
6371
- const stats = (0, import_fs5.statSync)(path48);
6372
- return createFileFromPath(path48, stats, filenameOrOptions, options);
6370
+ function fileFromPathSync(path49, filenameOrOptions, options = {}) {
6371
+ const stats = (0, import_fs5.statSync)(path49);
6372
+ return createFileFromPath(path49, stats, filenameOrOptions, options);
6373
6373
  }
6374
- async function fileFromPath2(path48, filenameOrOptions, options) {
6375
- const stats = await import_fs5.promises.stat(path48);
6376
- return createFileFromPath(path48, stats, filenameOrOptions, options);
6374
+ async function fileFromPath2(path49, filenameOrOptions, options) {
6375
+ const stats = await import_fs5.promises.stat(path49);
6376
+ return createFileFromPath(path49, stats, filenameOrOptions, options);
6377
6377
  }
6378
6378
  var import_fs5, import_path5, import_node_domexception, __classPrivateFieldSet5, __classPrivateFieldGet6, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
6379
6379
  var init_fileFromPath = __esm({
@@ -6433,6 +6433,412 @@ var init_fileFromPath = __esm({
6433
6433
  }
6434
6434
  });
6435
6435
 
6436
+ // src/core/control-config.ts
6437
+ function getControlApiUrl() {
6438
+ return process.env.HAR_CONTROL_API_URL ?? DEFAULT_CONTROL_API_URL;
6439
+ }
6440
+ function isControlEnabled() {
6441
+ return process.env.HAR_CONTROL_DISABLED !== "true";
6442
+ }
6443
+ var DEFAULT_CONTROL_API_URL;
6444
+ var init_control_config = __esm({
6445
+ "src/core/control-config.ts"() {
6446
+ "use strict";
6447
+ DEFAULT_CONTROL_API_URL = "http://localhost:3847";
6448
+ }
6449
+ });
6450
+
6451
+ // src/core/telemetry-config.ts
6452
+ function getPreferencePath() {
6453
+ if (process.env.HAR_TELEMETRY_CONFIG_PATH) {
6454
+ return path34.resolve(process.env.HAR_TELEMETRY_CONFIG_PATH);
6455
+ }
6456
+ return path34.join(os9.homedir(), ".har", "telemetry.json");
6457
+ }
6458
+ function parseEnvOverride(raw) {
6459
+ if (raw === void 0 || raw === "") return void 0;
6460
+ const normalized = raw.trim().toLowerCase();
6461
+ if (["0", "false", "off", "no", "disabled"].includes(normalized)) return false;
6462
+ if (["1", "true", "on", "yes", "enabled"].includes(normalized)) return true;
6463
+ return void 0;
6464
+ }
6465
+ function normalizeSignals(enabled, raw) {
6466
+ if (!enabled) return { ...DEFAULT_SIGNALS_OFF };
6467
+ return {
6468
+ metrics: raw?.metrics !== false,
6469
+ logs: raw?.logs !== false,
6470
+ prompts: raw?.prompts === true,
6471
+ traces: raw?.traces !== false
6472
+ };
6473
+ }
6474
+ function readTelemetryPreference() {
6475
+ const preferencePath = getPreferencePath();
6476
+ try {
6477
+ if (!fs34.existsSync(preferencePath)) {
6478
+ return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
6479
+ }
6480
+ const parsed = JSON.parse(fs34.readFileSync(preferencePath, "utf8"));
6481
+ const enabled = parsed.enabled !== false;
6482
+ return {
6483
+ enabled,
6484
+ signals: normalizeSignals(enabled, parsed.signals),
6485
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : void 0
6486
+ };
6487
+ } catch {
6488
+ return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
6489
+ }
6490
+ }
6491
+ function writeTelemetryPreference(enabled, signals) {
6492
+ const current = readTelemetryPreference();
6493
+ const nextSignals = normalizeSignals(enabled, {
6494
+ ...current.signals,
6495
+ ...signals
6496
+ });
6497
+ const preference = {
6498
+ enabled,
6499
+ signals: enabled ? nextSignals : { ...DEFAULT_SIGNALS_OFF },
6500
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
6501
+ };
6502
+ const preferencePath = getPreferencePath();
6503
+ fs34.mkdirSync(path34.dirname(preferencePath), { recursive: true });
6504
+ fs34.writeFileSync(preferencePath, JSON.stringify(preference, null, 2) + "\n");
6505
+ return preference;
6506
+ }
6507
+ function isTelemetryEnabled() {
6508
+ const envOverride = parseEnvOverride(process.env.HAR_TELEMETRY);
6509
+ if (envOverride !== void 0) return envOverride;
6510
+ return readTelemetryPreference().enabled;
6511
+ }
6512
+ function getTelemetrySignals() {
6513
+ if (!isTelemetryEnabled()) return { ...DEFAULT_SIGNALS_OFF };
6514
+ return readTelemetryPreference().signals;
6515
+ }
6516
+ function getTelemetryPreferencePath() {
6517
+ return getPreferencePath();
6518
+ }
6519
+ var fs34, os9, path34, DEFAULT_SIGNALS_ON, DEFAULT_SIGNALS_OFF, TELEMETRY_SIGNALS;
6520
+ var init_telemetry_config = __esm({
6521
+ "src/core/telemetry-config.ts"() {
6522
+ "use strict";
6523
+ fs34 = __toESM(require("fs"));
6524
+ os9 = __toESM(require("os"));
6525
+ path34 = __toESM(require("path"));
6526
+ DEFAULT_SIGNALS_ON = {
6527
+ metrics: true,
6528
+ logs: true,
6529
+ prompts: false,
6530
+ /** Hooks are traces-first; usage metrics are derived from span gen_ai.usage.* in Mission Control. */
6531
+ traces: true
6532
+ };
6533
+ DEFAULT_SIGNALS_OFF = {
6534
+ metrics: false,
6535
+ logs: false,
6536
+ prompts: false,
6537
+ traces: false
6538
+ };
6539
+ TELEMETRY_SIGNALS = [
6540
+ "Traces (default): Cursor / Claude / Codex activity via opentelemetry-hooks \u2192 Mission Control",
6541
+ "Logs/events (default): hook lifecycle logs without prompt bodies",
6542
+ "Metrics (default): token usage derived from span gen_ai.usage.* attributes",
6543
+ "Prompts (opt-in): user prompt text via IDE_OTEL_CAPTURE_TEXT \u2014 har telemetry on --prompts (also fills Mission Control purpose)",
6544
+ "Fallback: har control sync harvests local Claude/Codex session files when hooks telemetry is missing"
6545
+ ];
6546
+ }
6547
+ });
6548
+
6549
+ // src/core/otel-hooks.ts
6550
+ var otel_hooks_exports = {};
6551
+ __export(otel_hooks_exports, {
6552
+ OTEL_HOOKS_AGENTS: () => OTEL_HOOKS_AGENTS,
6553
+ OTEL_HOOKS_PACKAGE: () => OTEL_HOOKS_PACKAGE,
6554
+ buildOtelHooksConfig: () => buildOtelHooksConfig,
6555
+ disableOtelHooksExport: () => disableOtelHooksExport,
6556
+ ensureOtelHooks: () => ensureOtelHooks,
6557
+ getOtelHooksConfigPath: () => getOtelHooksConfigPath,
6558
+ getOtelHooksHome: () => getOtelHooksHome,
6559
+ getOtelHooksWrapperPath: () => getOtelHooksWrapperPath,
6560
+ rewriteHookCommandsToWrapper: () => rewriteHookCommandsToWrapper,
6561
+ writeOtelHooksConfig: () => writeOtelHooksConfig,
6562
+ writeOtelHooksWrapper: () => writeOtelHooksWrapper
6563
+ });
6564
+ function getOtelHooksHome() {
6565
+ if (process.env.HAR_OTEL_HOOKS_HOME) {
6566
+ return path40.resolve(process.env.HAR_OTEL_HOOKS_HOME);
6567
+ }
6568
+ return path40.join(os12.homedir(), ".har", "otel-hooks");
6569
+ }
6570
+ function getOtelHooksConfigPath(hooksHome = getOtelHooksHome()) {
6571
+ return path40.join(hooksHome, "otel_config.json");
6572
+ }
6573
+ function getOtelHooksWrapperPath(hooksHome = getOtelHooksHome()) {
6574
+ return path40.join(hooksHome, "run-otel-hook.sh");
6575
+ }
6576
+ function buildOtelHooksConfig(options) {
6577
+ const enabled = options?.enabled ?? isTelemetryEnabled();
6578
+ const signals = getTelemetrySignals();
6579
+ const apiUrl = (options?.apiUrl ?? getControlApiUrl()).replace(/\/$/, "");
6580
+ return {
6581
+ OTEL_EXPORTER_OTLP_ENDPOINT: enabled ? `${apiUrl}/api/otel/v1/traces` : null,
6582
+ OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
6583
+ OTEL_SERVICE_NAME: "har-ide-agent",
6584
+ IDE_OTEL_BATCH_ON_STOP: "true",
6585
+ IDE_OTEL_ENABLE_LOGS: enabled && signals.logs ? "true" : "false",
6586
+ IDE_OTEL_LOG_ALL_EVENTS: enabled && signals.logs ? "true" : "false",
6587
+ IDE_OTEL_CAPTURE_TEXT: enabled && signals.prompts ? "true" : "false",
6588
+ IDE_OTEL_MASK_PROMPTS: "false",
6589
+ OTEL_RESOURCE_ATTRIBUTES: options?.resourceAttributes ?? null
6590
+ };
6591
+ }
6592
+ function writeOtelHooksConfig(config2, hooksHome = getOtelHooksHome()) {
6593
+ fs38.mkdirSync(hooksHome, { recursive: true });
6594
+ const configPath = getOtelHooksConfigPath(hooksHome);
6595
+ const payload = {
6596
+ _comment: "Managed by har telemetry \u2014 do not edit by hand; use: har telemetry on|off",
6597
+ ...config2
6598
+ };
6599
+ fs38.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}
6600
+ `);
6601
+ return configPath;
6602
+ }
6603
+ function writeOtelHooksWrapper(otelHookCommand, hooksHome = getOtelHooksHome()) {
6604
+ fs38.mkdirSync(hooksHome, { recursive: true });
6605
+ const wrapperPath = getOtelHooksWrapperPath(hooksHome);
6606
+ const script = `#!/usr/bin/env bash
6607
+ # Managed by har telemetry \u2014 invokes opentelemetry-hooks with HAR config home.
6608
+ set -euo pipefail
6609
+ export IDE_OTEL_HOOK_HOME="${hooksHome}"
6610
+ exec "${otelHookCommand}" "$@"
6611
+ `;
6612
+ fs38.writeFileSync(wrapperPath, script, { mode: 493 });
6613
+ try {
6614
+ fs38.chmodSync(wrapperPath, 493);
6615
+ } catch {
6616
+ }
6617
+ return wrapperPath;
6618
+ }
6619
+ function commandExists(command2) {
6620
+ const result = (0, import_child_process9.spawnSync)("sh", ["-c", `command -v ${JSON.stringify(command2)}`], {
6621
+ encoding: "utf8"
6622
+ });
6623
+ return result.status === 0 && Boolean(result.stdout?.trim());
6624
+ }
6625
+ function resolveOtelHookCommand() {
6626
+ if (commandExists("otel-hook")) {
6627
+ const which = (0, import_child_process9.spawnSync)("sh", ["-c", "command -v otel-hook"], { encoding: "utf8" });
6628
+ return which.stdout?.trim() || "otel-hook";
6629
+ }
6630
+ return null;
6631
+ }
6632
+ function tryInstallPackage() {
6633
+ if (commandExists("uv")) {
6634
+ const result = (0, import_child_process9.spawnSync)(
6635
+ "uv",
6636
+ ["tool", "install", "--force", OTEL_HOOKS_PACKAGE],
6637
+ { encoding: "utf8", timeout: 18e4 }
6638
+ );
6639
+ if (result.status === 0) {
6640
+ return { ok: true, detail: `uv tool install ${OTEL_HOOKS_PACKAGE}` };
6641
+ }
6642
+ }
6643
+ if (commandExists("pipx")) {
6644
+ const result = (0, import_child_process9.spawnSync)("pipx", ["install", "--force", OTEL_HOOKS_PACKAGE], {
6645
+ encoding: "utf8",
6646
+ timeout: 18e4
6647
+ });
6648
+ if (result.status === 0) {
6649
+ return { ok: true, detail: `pipx install ${OTEL_HOOKS_PACKAGE}` };
6650
+ }
6651
+ return {
6652
+ ok: false,
6653
+ detail: `pipx install failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
6654
+ };
6655
+ }
6656
+ if (commandExists("pip3") || commandExists("pip")) {
6657
+ const pip = commandExists("pip3") ? "pip3" : "pip";
6658
+ const result = (0, import_child_process9.spawnSync)(
6659
+ pip,
6660
+ ["install", "--user", OTEL_HOOKS_PACKAGE],
6661
+ { encoding: "utf8", timeout: 18e4 }
6662
+ );
6663
+ if (result.status === 0) {
6664
+ return { ok: true, detail: `${pip} install --user ${OTEL_HOOKS_PACKAGE}` };
6665
+ }
6666
+ return {
6667
+ ok: false,
6668
+ detail: `${pip} install failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
6669
+ };
6670
+ }
6671
+ return {
6672
+ ok: false,
6673
+ detail: "Neither uv, pipx, nor pip3/pip found; install Python tooling to use opentelemetry-hooks"
6674
+ };
6675
+ }
6676
+ function runSetupAgent(agent, otelHookCommand, hooksHome) {
6677
+ const result = (0, import_child_process9.spawnSync)(otelHookCommand, ["setup", "--agent", agent], {
6678
+ encoding: "utf8",
6679
+ env: {
6680
+ ...process.env,
6681
+ IDE_OTEL_HOOK_HOME: hooksHome
6682
+ },
6683
+ timeout: 6e4
6684
+ });
6685
+ if (result.status === 0) {
6686
+ return { ok: true, detail: `setup --agent ${agent}` };
6687
+ }
6688
+ return {
6689
+ ok: false,
6690
+ detail: `setup --agent ${agent} failed: ${(result.stderr || result.stdout || "").trim().slice(0, 400)}`
6691
+ };
6692
+ }
6693
+ function rewriteHookCommandsToWrapper(filePath, wrapperPath) {
6694
+ if (!fs38.existsSync(filePath)) return false;
6695
+ let raw;
6696
+ try {
6697
+ raw = fs38.readFileSync(filePath, "utf8");
6698
+ } catch {
6699
+ return false;
6700
+ }
6701
+ let parsed;
6702
+ try {
6703
+ parsed = JSON.parse(raw);
6704
+ } catch {
6705
+ return false;
6706
+ }
6707
+ if (!parsed || typeof parsed !== "object") return false;
6708
+ const quoted = JSON.stringify(wrapperPath);
6709
+ const rewriteCommand = (command2) => {
6710
+ if (!command2.includes("otel-hook") && !command2.includes("otel_hook.py")) return command2;
6711
+ if (command2.includes(wrapperPath)) return command2;
6712
+ return command2.replace(
6713
+ /(?:^|[;&|]\s*)(?:env\s+[^=\s]+=\S+\s+)*(?:python3?\s+)?(?:\S*otel[_-]hook\S*)/,
6714
+ (match) => match.replace(/(?:python3?\s+)?(?:\S*otel[_-]hook\S*)/, quoted.slice(1, -1))
6715
+ );
6716
+ };
6717
+ const walk = (node) => {
6718
+ let changed = false;
6719
+ if (Array.isArray(node)) {
6720
+ for (const item of node) changed = walk(item) || changed;
6721
+ return changed;
6722
+ }
6723
+ if (!node || typeof node !== "object") return false;
6724
+ const obj = node;
6725
+ if (typeof obj.command === "string") {
6726
+ const next = rewriteCommand(obj.command);
6727
+ if (next !== obj.command) {
6728
+ obj.command = next;
6729
+ changed = true;
6730
+ }
6731
+ }
6732
+ for (const value of Object.values(obj)) changed = walk(value) || changed;
6733
+ return changed;
6734
+ };
6735
+ if (!walk(parsed)) return false;
6736
+ fs38.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
6737
+ `);
6738
+ return true;
6739
+ }
6740
+ function rewriteKnownHookRegistrations(wrapperPath) {
6741
+ const home = os12.homedir();
6742
+ const candidates = [
6743
+ path40.join(home, ".cursor", "hooks.json"),
6744
+ path40.join(home, ".claude", "settings.json"),
6745
+ path40.join(home, ".codex", "hooks.json")
6746
+ ];
6747
+ for (const filePath of candidates) {
6748
+ try {
6749
+ rewriteHookCommandsToWrapper(filePath, wrapperPath);
6750
+ } catch {
6751
+ }
6752
+ }
6753
+ }
6754
+ function ensureOtelHooks(options) {
6755
+ const hooksHome = getOtelHooksHome();
6756
+ const errors = [];
6757
+ const agentsSetup = [];
6758
+ fs38.mkdirSync(hooksHome, { recursive: true });
6759
+ let otelHookCommand = resolveOtelHookCommand();
6760
+ if (!otelHookCommand) {
6761
+ const installed = tryInstallPackage();
6762
+ if (!installed.ok) {
6763
+ errors.push(installed.detail);
6764
+ const configPath2 = writeOtelHooksConfig(
6765
+ buildOtelHooksConfig({ resourceAttributes: options?.resourceAttributes }),
6766
+ hooksHome
6767
+ );
6768
+ return {
6769
+ ok: false,
6770
+ hooksHome,
6771
+ configPath: configPath2,
6772
+ wrapperPath: getOtelHooksWrapperPath(hooksHome),
6773
+ otelHookCommand: null,
6774
+ warning: installed.detail,
6775
+ agentsSetup,
6776
+ errors
6777
+ };
6778
+ }
6779
+ otelHookCommand = resolveOtelHookCommand();
6780
+ if (!otelHookCommand) {
6781
+ errors.push("opentelemetry-hooks installed but otel-hook is not on PATH");
6782
+ const configPath2 = writeOtelHooksConfig(
6783
+ buildOtelHooksConfig({ resourceAttributes: options?.resourceAttributes }),
6784
+ hooksHome
6785
+ );
6786
+ return {
6787
+ ok: false,
6788
+ hooksHome,
6789
+ configPath: configPath2,
6790
+ wrapperPath: getOtelHooksWrapperPath(hooksHome),
6791
+ otelHookCommand: null,
6792
+ warning: errors[errors.length - 1],
6793
+ agentsSetup,
6794
+ errors
6795
+ };
6796
+ }
6797
+ }
6798
+ const configPath = writeOtelHooksConfig(
6799
+ buildOtelHooksConfig({ resourceAttributes: options?.resourceAttributes }),
6800
+ hooksHome
6801
+ );
6802
+ const wrapperPath = writeOtelHooksWrapper(otelHookCommand, hooksHome);
6803
+ if (options?.setupAgents !== false && isTelemetryEnabled()) {
6804
+ for (const agent of OTEL_HOOKS_AGENTS) {
6805
+ const setup = runSetupAgent(agent, otelHookCommand, hooksHome);
6806
+ if (setup.ok) agentsSetup.push(agent);
6807
+ else errors.push(setup.detail);
6808
+ }
6809
+ rewriteKnownHookRegistrations(wrapperPath);
6810
+ }
6811
+ const ok = errors.length === 0;
6812
+ return {
6813
+ ok,
6814
+ hooksHome,
6815
+ configPath,
6816
+ wrapperPath,
6817
+ otelHookCommand,
6818
+ message: ok ? `opentelemetry-hooks ready (${agentsSetup.join(", ") || "config only"}) \u2192 ${configPath}` : void 0,
6819
+ warning: errors.length ? errors.join("; ") : void 0,
6820
+ agentsSetup,
6821
+ errors
6822
+ };
6823
+ }
6824
+ function disableOtelHooksExport() {
6825
+ return writeOtelHooksConfig(buildOtelHooksConfig({ enabled: false }));
6826
+ }
6827
+ var import_child_process9, fs38, os12, path40, OTEL_HOOKS_PACKAGE, OTEL_HOOKS_AGENTS;
6828
+ var init_otel_hooks = __esm({
6829
+ "src/core/otel-hooks.ts"() {
6830
+ "use strict";
6831
+ import_child_process9 = require("child_process");
6832
+ fs38 = __toESM(require("fs"));
6833
+ os12 = __toESM(require("os"));
6834
+ path40 = __toESM(require("path"));
6835
+ init_control_config();
6836
+ init_telemetry_config();
6837
+ OTEL_HOOKS_PACKAGE = "opentelemetry-hooks==0.14.0";
6838
+ OTEL_HOOKS_AGENTS = ["cursor", "claude", "codex"];
6839
+ }
6840
+ });
6841
+
6436
6842
  // node_modules/ajv/dist/compile/codegen/code.js
6437
6843
  var require_code = __commonJS({
6438
6844
  "node_modules/ajv/dist/compile/codegen/code.js"(exports2) {
@@ -9384,7 +9790,7 @@ var require_compile = __commonJS({
9384
9790
  const schOrFunc = root.refs[ref];
9385
9791
  if (schOrFunc)
9386
9792
  return schOrFunc;
9387
- let _sch = resolve33.call(this, root, ref);
9793
+ let _sch = resolve34.call(this, root, ref);
9388
9794
  if (_sch === void 0) {
9389
9795
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
9390
9796
  const { schemaId } = this.opts;
@@ -9411,7 +9817,7 @@ var require_compile = __commonJS({
9411
9817
  function sameSchemaEnv(s1, s2) {
9412
9818
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
9413
9819
  }
9414
- function resolve33(root, ref) {
9820
+ function resolve34(root, ref) {
9415
9821
  let sch;
9416
9822
  while (typeof (sch = this.refs[ref]) == "string")
9417
9823
  ref = sch;
@@ -9629,8 +10035,8 @@ var require_utils2 = __commonJS({
9629
10035
  }
9630
10036
  return ind;
9631
10037
  }
9632
- function removeDotSegments(path48) {
9633
- let input = path48;
10038
+ function removeDotSegments(path49) {
10039
+ let input = path49;
9634
10040
  const output = [];
9635
10041
  let nextSlash = -1;
9636
10042
  let len = 0;
@@ -9882,8 +10288,8 @@ var require_schemes = __commonJS({
9882
10288
  wsComponent.secure = void 0;
9883
10289
  }
9884
10290
  if (wsComponent.resourceName) {
9885
- const [path48, query] = wsComponent.resourceName.split("?");
9886
- wsComponent.path = path48 && path48 !== "/" ? path48 : void 0;
10291
+ const [path49, query] = wsComponent.resourceName.split("?");
10292
+ wsComponent.path = path49 && path49 !== "/" ? path49 : void 0;
9887
10293
  wsComponent.query = query;
9888
10294
  wsComponent.resourceName = void 0;
9889
10295
  }
@@ -10042,7 +10448,7 @@ var require_fast_uri = __commonJS({
10042
10448
  }
10043
10449
  return uri;
10044
10450
  }
10045
- function resolve33(baseURI, relativeURI, options) {
10451
+ function resolve34(baseURI, relativeURI, options) {
10046
10452
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
10047
10453
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
10048
10454
  schemelessOptions.skipEscape = true;
@@ -10300,7 +10706,7 @@ var require_fast_uri = __commonJS({
10300
10706
  var fastUri = {
10301
10707
  SCHEMES,
10302
10708
  normalize: normalize3,
10303
- resolve: resolve33,
10709
+ resolve: resolve34,
10304
10710
  resolveComponent,
10305
10711
  equal,
10306
10712
  serialize,
@@ -14565,11 +14971,11 @@ var parser = new YargsParser({
14565
14971
  resolve: import_path2.resolve,
14566
14972
  // TODO: figure out a way to combine ESM and CJS coverage, such that
14567
14973
  // we can exercise all the lines below:
14568
- require: (path48) => {
14974
+ require: (path49) => {
14569
14975
  if (typeof require !== "undefined") {
14570
- return require(path48);
14571
- } else if (path48.match(/\.json$/)) {
14572
- return JSON.parse((0, import_fs2.readFileSync)(path48, "utf8"));
14976
+ return require(path49);
14977
+ } else if (path49.match(/\.json$/)) {
14978
+ return JSON.parse((0, import_fs2.readFileSync)(path49, "utf8"));
14573
14979
  } else {
14574
14980
  throw Error("only .json config files are supported in ESM");
14575
14981
  }
@@ -17095,12 +17501,12 @@ var YargsInstance = class {
17095
17501
  async getCompletion(args, done) {
17096
17502
  argsert("<array> [function]", [args, done], arguments.length);
17097
17503
  if (!done) {
17098
- return new Promise((resolve33, reject) => {
17504
+ return new Promise((resolve34, reject) => {
17099
17505
  __classPrivateFieldGet(this, _YargsInstance_completion, "f").getCompletion(args, (err, completions) => {
17100
17506
  if (err)
17101
17507
  reject(err);
17102
17508
  else
17103
- resolve33(completions);
17509
+ resolve34(completions);
17104
17510
  });
17105
17511
  });
17106
17512
  } else {
@@ -19250,8 +19656,8 @@ function getErrorMap() {
19250
19656
 
19251
19657
  // node_modules/zod/v3/helpers/parseUtil.js
19252
19658
  var makeIssue = (params) => {
19253
- const { data, path: path48, errorMaps, issueData } = params;
19254
- const fullPath = [...path48, ...issueData.path || []];
19659
+ const { data, path: path49, errorMaps, issueData } = params;
19660
+ const fullPath = [...path49, ...issueData.path || []];
19255
19661
  const fullIssue = {
19256
19662
  ...issueData,
19257
19663
  path: fullPath
@@ -19367,11 +19773,11 @@ var errorUtil;
19367
19773
 
19368
19774
  // node_modules/zod/v3/types.js
19369
19775
  var ParseInputLazyPath = class {
19370
- constructor(parent, value, path48, key) {
19776
+ constructor(parent, value, path49, key) {
19371
19777
  this._cachedPath = [];
19372
19778
  this.parent = parent;
19373
19779
  this.data = value;
19374
- this._path = path48;
19780
+ this._path = path49;
19375
19781
  this._key = key;
19376
19782
  }
19377
19783
  get path() {
@@ -22992,7 +23398,6 @@ var SlotRegistryEntrySchema = external_exports.object({
22992
23398
  /** Random per-session chars; absent in root mode. */
22993
23399
  suffix: external_exports.string().optional(),
22994
23400
  createdAt: external_exports.string(),
22995
- purpose: external_exports.string().optional(),
22996
23401
  ports: external_exports.record(external_exports.number()).optional(),
22997
23402
  previewUrls: external_exports.record(external_exports.string()).optional(),
22998
23403
  status: external_exports.enum(["starting", "active", "failed", "completed"]).default("active"),
@@ -23038,6 +23443,7 @@ var AgentSlotStatusSchema = external_exports.object({
23038
23443
  baseBranch: external_exports.string().optional(),
23039
23444
  baseCommit: external_exports.string().optional(),
23040
23445
  sessionCreatedAt: external_exports.string().optional(),
23446
+ /** Derived from first captured user prompt (OTEL hooks); not set at launch. */
23041
23447
  purpose: external_exports.string().optional(),
23042
23448
  sessionStatus: external_exports.enum(["starting", "active", "failed", "completed"]).optional(),
23043
23449
  lastError: external_exports.string().optional(),
@@ -23106,7 +23512,7 @@ var ValidationRecordSchema = external_exports.object({
23106
23512
  var SyncValidationsInputSchema = external_exports.object({
23107
23513
  validations: external_exports.array(ValidationRecordSchema)
23108
23514
  });
23109
- var AgentToolSchema = external_exports.enum(["claude_code", "codex"]);
23515
+ var AgentToolSchema = external_exports.enum(["claude_code", "codex", "cursor"]);
23110
23516
  var UsageSourceSchema = external_exports.enum(["otel", "harvest"]);
23111
23517
  var AgentSessionUsageSchema = external_exports.object({
23112
23518
  sessionKey: external_exports.string().min(1),
@@ -23410,16 +23816,16 @@ async function handleAgentSkills(options) {
23410
23816
  }
23411
23817
  async function askYesNo(question) {
23412
23818
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
23413
- return new Promise((resolve33) => {
23819
+ return new Promise((resolve34) => {
23414
23820
  process.stderr.write(`${question} `);
23415
23821
  rl.once("line", (answer) => {
23416
23822
  rl.close();
23417
23823
  const trimmed = answer.trim();
23418
23824
  if (trimmed === "") {
23419
- resolve33(true);
23825
+ resolve34(true);
23420
23826
  return;
23421
23827
  }
23422
- resolve33(/^y(es)?$/i.test(trimmed));
23828
+ resolve34(/^y(es)?$/i.test(trimmed));
23423
23829
  });
23424
23830
  });
23425
23831
  }
@@ -23498,7 +23904,7 @@ function handleRemove(argv) {
23498
23904
  }
23499
23905
 
23500
23906
  // src/cli/commands/env.ts
23501
- var path41 = __toESM(require("path"));
23907
+ var path42 = __toESM(require("path"));
23502
23908
 
23503
23909
  // src/core/harness.ts
23504
23910
  var fs20 = __toESM(require("fs"));
@@ -24277,13 +24683,13 @@ var MultipartBody = class {
24277
24683
  // node_modules/@anthropic-ai/sdk/_shims/node-runtime.mjs
24278
24684
  var import_web = require("node:stream/web");
24279
24685
  var fileFromPathWarned = false;
24280
- async function fileFromPath3(path48, ...args) {
24686
+ async function fileFromPath3(path49, ...args) {
24281
24687
  const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => (init_fileFromPath(), fileFromPath_exports));
24282
24688
  if (!fileFromPathWarned) {
24283
- console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path48)}) instead`);
24689
+ console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path49)}) instead`);
24284
24690
  fileFromPathWarned = true;
24285
24691
  }
24286
- return await _fileFromPath(path48, ...args);
24692
+ return await _fileFromPath(path49, ...args);
24287
24693
  }
24288
24694
  var defaultHttpAgent = new import_agentkeepalive.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
24289
24695
  var defaultHttpsAgent = new import_agentkeepalive.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
@@ -24779,8 +25185,8 @@ async function defaultParseResponse(props) {
24779
25185
  }
24780
25186
  var APIPromise = class _APIPromise extends Promise {
24781
25187
  constructor(responsePromise, parseResponse = defaultParseResponse) {
24782
- super((resolve33) => {
24783
- resolve33(null);
25188
+ super((resolve34) => {
25189
+ resolve34(null);
24784
25190
  });
24785
25191
  this.responsePromise = responsePromise;
24786
25192
  this.parseResponse = parseResponse;
@@ -24880,29 +25286,29 @@ var APIClient = class {
24880
25286
  defaultIdempotencyKey() {
24881
25287
  return `stainless-node-retry-${uuid4()}`;
24882
25288
  }
24883
- get(path48, opts) {
24884
- return this.methodRequest("get", path48, opts);
25289
+ get(path49, opts) {
25290
+ return this.methodRequest("get", path49, opts);
24885
25291
  }
24886
- post(path48, opts) {
24887
- return this.methodRequest("post", path48, opts);
25292
+ post(path49, opts) {
25293
+ return this.methodRequest("post", path49, opts);
24888
25294
  }
24889
- patch(path48, opts) {
24890
- return this.methodRequest("patch", path48, opts);
25295
+ patch(path49, opts) {
25296
+ return this.methodRequest("patch", path49, opts);
24891
25297
  }
24892
- put(path48, opts) {
24893
- return this.methodRequest("put", path48, opts);
25298
+ put(path49, opts) {
25299
+ return this.methodRequest("put", path49, opts);
24894
25300
  }
24895
- delete(path48, opts) {
24896
- return this.methodRequest("delete", path48, opts);
25301
+ delete(path49, opts) {
25302
+ return this.methodRequest("delete", path49, opts);
24897
25303
  }
24898
- methodRequest(method, path48, opts) {
25304
+ methodRequest(method, path49, opts) {
24899
25305
  return this.request(Promise.resolve(opts).then(async (opts2) => {
24900
25306
  const body = opts2 && isBlobLike(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
24901
- return { method, path: path48, ...opts2, body };
25307
+ return { method, path: path49, ...opts2, body };
24902
25308
  }));
24903
25309
  }
24904
- getAPIList(path48, Page, opts) {
24905
- return this.requestAPIList(Page, { method: "get", path: path48, ...opts });
25310
+ getAPIList(path49, Page, opts) {
25311
+ return this.requestAPIList(Page, { method: "get", path: path49, ...opts });
24906
25312
  }
24907
25313
  calculateContentLength(body) {
24908
25314
  if (typeof body === "string") {
@@ -24920,10 +25326,10 @@ var APIClient = class {
24920
25326
  return null;
24921
25327
  }
24922
25328
  buildRequest(options) {
24923
- const { method, path: path48, query, headers = {} } = options;
25329
+ const { method, path: path49, query, headers = {} } = options;
24924
25330
  const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : isMultipartBody(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
24925
25331
  const contentLength = this.calculateContentLength(body);
24926
- const url = this.buildURL(path48, query);
25332
+ const url = this.buildURL(path49, query);
24927
25333
  if ("timeout" in options)
24928
25334
  validatePositiveInteger("timeout", options.timeout);
24929
25335
  const timeout = options.timeout ?? this.timeout;
@@ -25032,8 +25438,8 @@ var APIClient = class {
25032
25438
  const request = this.makeRequest(options, null);
25033
25439
  return new PagePromise(this, request, Page);
25034
25440
  }
25035
- buildURL(path48, query) {
25036
- const url = isAbsoluteURL(path48) ? new URL(path48) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path48.startsWith("/") ? path48.slice(1) : path48));
25441
+ buildURL(path49, query) {
25442
+ const url = isAbsoluteURL(path49) ? new URL(path49) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path49.startsWith("/") ? path49.slice(1) : path49));
25037
25443
  const defaultQuery = this.defaultQuery();
25038
25444
  if (!isEmptyObj(defaultQuery)) {
25039
25445
  query = { ...defaultQuery, ...query };
@@ -25318,7 +25724,7 @@ var startsWithSchemeRegexp = new RegExp("^(?:[a-z]+:)?//", "i");
25318
25724
  var isAbsoluteURL = (url) => {
25319
25725
  return startsWithSchemeRegexp.test(url);
25320
25726
  };
25321
- var sleep = (ms) => new Promise((resolve33) => setTimeout(resolve33, ms));
25727
+ var sleep = (ms) => new Promise((resolve34) => setTimeout(resolve34, ms));
25322
25728
  var validatePositiveInteger = (name, n2) => {
25323
25729
  if (typeof n2 !== "number" || !Number.isInteger(n2)) {
25324
25730
  throw new AnthropicError(`${name} must be an integer`);
@@ -25809,12 +26215,12 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25809
26215
  }
25810
26216
  return this._emit("error", new AnthropicError(String(error3)));
25811
26217
  });
25812
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve33, reject) => {
25813
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve33, "f");
26218
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_connectedPromise, new Promise((resolve34, reject) => {
26219
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveConnectedPromise, resolve34, "f");
25814
26220
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectConnectedPromise, reject, "f");
25815
26221
  }), "f");
25816
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve33, reject) => {
25817
- __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve33, "f");
26222
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_endPromise, new Promise((resolve34, reject) => {
26223
+ __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_resolveEndPromise, resolve34, "f");
25818
26224
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_rejectEndPromise, reject, "f");
25819
26225
  }), "f");
25820
26226
  __classPrivateFieldGet8(this, _PromptCachingBetaMessageStream_connectedPromise, "f").catch(() => {
@@ -25943,11 +26349,11 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
25943
26349
  * const message = await stream.emitted('message') // rejects if the stream errors
25944
26350
  */
25945
26351
  emitted(event) {
25946
- return new Promise((resolve33, reject) => {
26352
+ return new Promise((resolve34, reject) => {
25947
26353
  __classPrivateFieldSet7(this, _PromptCachingBetaMessageStream_catchingPromiseCreated, true, "f");
25948
26354
  if (event !== "error")
25949
26355
  this.once("error", reject);
25950
- this.once(event, resolve33);
26356
+ this.once(event, resolve34);
25951
26357
  });
25952
26358
  }
25953
26359
  async done() {
@@ -26174,7 +26580,7 @@ var PromptCachingBetaMessageStream = class _PromptCachingBetaMessageStream {
26174
26580
  if (done) {
26175
26581
  return { value: void 0, done: true };
26176
26582
  }
26177
- return new Promise((resolve33, reject) => readQueue.push({ resolve: resolve33, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26583
+ return new Promise((resolve34, reject) => readQueue.push({ resolve: resolve34, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26178
26584
  }
26179
26585
  const chunk = pushQueue.shift();
26180
26586
  return { value: chunk, done: false };
@@ -26322,12 +26728,12 @@ var MessageStream = class _MessageStream {
26322
26728
  }
26323
26729
  return this._emit("error", new AnthropicError(String(error3)));
26324
26730
  });
26325
- __classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve33, reject) => {
26326
- __classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve33, "f");
26731
+ __classPrivateFieldSet8(this, _MessageStream_connectedPromise, new Promise((resolve34, reject) => {
26732
+ __classPrivateFieldSet8(this, _MessageStream_resolveConnectedPromise, resolve34, "f");
26327
26733
  __classPrivateFieldSet8(this, _MessageStream_rejectConnectedPromise, reject, "f");
26328
26734
  }), "f");
26329
- __classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve33, reject) => {
26330
- __classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve33, "f");
26735
+ __classPrivateFieldSet8(this, _MessageStream_endPromise, new Promise((resolve34, reject) => {
26736
+ __classPrivateFieldSet8(this, _MessageStream_resolveEndPromise, resolve34, "f");
26331
26737
  __classPrivateFieldSet8(this, _MessageStream_rejectEndPromise, reject, "f");
26332
26738
  }), "f");
26333
26739
  __classPrivateFieldGet9(this, _MessageStream_connectedPromise, "f").catch(() => {
@@ -26456,11 +26862,11 @@ var MessageStream = class _MessageStream {
26456
26862
  * const message = await stream.emitted('message') // rejects if the stream errors
26457
26863
  */
26458
26864
  emitted(event) {
26459
- return new Promise((resolve33, reject) => {
26865
+ return new Promise((resolve34, reject) => {
26460
26866
  __classPrivateFieldSet8(this, _MessageStream_catchingPromiseCreated, true, "f");
26461
26867
  if (event !== "error")
26462
26868
  this.once("error", reject);
26463
- this.once(event, resolve33);
26869
+ this.once(event, resolve34);
26464
26870
  });
26465
26871
  }
26466
26872
  async done() {
@@ -26687,7 +27093,7 @@ var MessageStream = class _MessageStream {
26687
27093
  if (done) {
26688
27094
  return { value: void 0, done: true };
26689
27095
  }
26690
- return new Promise((resolve33, reject) => readQueue.push({ resolve: resolve33, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
27096
+ return new Promise((resolve34, reject) => readQueue.push({ resolve: resolve34, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
26691
27097
  }
26692
27098
  const chunk = pushQueue.shift();
26693
27099
  return { value: chunk, done: false };
@@ -26947,11 +27353,11 @@ async function promptApplyAgentMdProposal(repoPath) {
26947
27353
  }
26948
27354
  async function askYesNo2(question) {
26949
27355
  const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
26950
- return new Promise((resolve33) => {
27356
+ return new Promise((resolve34) => {
26951
27357
  process.stderr.write(`${question} `);
26952
27358
  rl.once("line", (answer) => {
26953
27359
  rl.close();
26954
- resolve33(/^y(es)?$/i.test(answer.trim()));
27360
+ resolve34(/^y(es)?$/i.test(answer.trim()));
26955
27361
  });
26956
27362
  });
26957
27363
  }
@@ -27181,7 +27587,7 @@ async function executeAuthoringToolCall(toolName, input, repoPath, harnessDir, c
27181
27587
  }
27182
27588
  async function askUser(question, options) {
27183
27589
  const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
27184
- return new Promise((resolve33) => {
27590
+ return new Promise((resolve34) => {
27185
27591
  process.stderr.write("\n");
27186
27592
  process.stderr.write(`\u2753 ${question}
27187
27593
  `);
@@ -27192,7 +27598,7 @@ async function askUser(question, options) {
27192
27598
  process.stderr.write("> ");
27193
27599
  rl.once("line", (answer) => {
27194
27600
  rl.close();
27195
- resolve33(answer.trim());
27601
+ resolve34(answer.trim());
27196
27602
  });
27197
27603
  });
27198
27604
  }
@@ -27336,7 +27742,7 @@ function runScript(scriptPath, args = [], options = {}) {
27336
27742
  function runScriptCapture(scriptPath, args = [], options = {}) {
27337
27743
  const stream = options.stream ?? false;
27338
27744
  const { stream: _stream, ...spawnOptions } = options;
27339
- return new Promise((resolve33) => {
27745
+ return new Promise((resolve34) => {
27340
27746
  const proc = (0, import_child_process.spawn)("bash", [scriptPath, ...args], {
27341
27747
  ...spawnOptions,
27342
27748
  stdio: ["inherit", "pipe", "pipe"]
@@ -27351,13 +27757,13 @@ function runScriptCapture(scriptPath, args = [], options = {}) {
27351
27757
  stderr += d2;
27352
27758
  if (stream) process.stderr.write(d2);
27353
27759
  });
27354
- proc.on("close", (code) => resolve33({ stdout, stderr, code: code ?? 0 }));
27760
+ proc.on("close", (code) => resolve34({ stdout, stderr, code: code ?? 0 }));
27355
27761
  });
27356
27762
  }
27357
27763
  function runShellCommand(command2, options = {}) {
27358
27764
  const stream = options.stream ?? false;
27359
27765
  const { stream: _stream, ...spawnOptions } = options;
27360
- return new Promise((resolve33) => {
27766
+ return new Promise((resolve34) => {
27361
27767
  const proc = (0, import_child_process.spawn)("bash", ["-lc", command2], {
27362
27768
  ...spawnOptions,
27363
27769
  stdio: ["inherit", "pipe", "pipe"]
@@ -27372,7 +27778,7 @@ function runShellCommand(command2, options = {}) {
27372
27778
  stderr += d2;
27373
27779
  if (stream) process.stderr.write(d2);
27374
27780
  });
27375
- proc.on("close", (code) => resolve33({ stdout, stderr, code: code ?? 0 }));
27781
+ proc.on("close", (code) => resolve34({ stdout, stderr, code: code ?? 0 }));
27376
27782
  });
27377
27783
  }
27378
27784
 
@@ -28536,16 +28942,16 @@ async function promptScaffoldCursorRule(exists) {
28536
28942
  }
28537
28943
  async function askYesNo3(question) {
28538
28944
  const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
28539
- return new Promise((resolve33) => {
28945
+ return new Promise((resolve34) => {
28540
28946
  process.stderr.write(`${question} `);
28541
28947
  rl.once("line", (answer) => {
28542
28948
  rl.close();
28543
28949
  const trimmed = answer.trim();
28544
28950
  if (trimmed === "") {
28545
- resolve33(true);
28951
+ resolve34(true);
28546
28952
  return;
28547
28953
  }
28548
- resolve33(/^y(es)?$/i.test(trimmed));
28954
+ resolve34(/^y(es)?$/i.test(trimmed));
28549
28955
  });
28550
28956
  });
28551
28957
  }
@@ -28663,7 +29069,6 @@ function buildLaunchFlagArgs(flags) {
28663
29069
  if (flags.confirmReplace) args.push("--replace");
28664
29070
  if (flags.force) args.push("--force");
28665
29071
  if (flags.resume) args.push("--resume");
28666
- if (flags.purpose) args.push(`--purpose=${flags.purpose}`);
28667
29072
  return args;
28668
29073
  }
28669
29074
  function resolveRepoPath(repoPath) {
@@ -28744,9 +29149,6 @@ function buildExecutionPlan(repoPath, stage, options) {
28744
29149
  const cwd = stage.cwd ? path24.resolve(resolvedRepo, stage.cwd) : resolvedRepo;
28745
29150
  const extraArgs = options.args ?? [];
28746
29151
  const launchFlagArgs = stage.kind === "launch" && options.launchFlags ? buildLaunchFlagArgs(options.launchFlags) : [];
28747
- if (stage.kind === "launch" && options.launchFlags?.purpose) {
28748
- mergedEnv.HAR_SESSION_PURPOSE = options.launchFlags.purpose;
28749
- }
28750
29152
  if (stage.command) {
28751
29153
  let shellCommand = substituteAgentId(stage.command, options.agentId);
28752
29154
  for (const extra of [...extraArgs, ...launchFlagArgs]) {
@@ -28843,15 +29245,7 @@ var localScriptExecutor = new LocalScriptExecutor();
28843
29245
 
28844
29246
  // src/core/control-sync.ts
28845
29247
  var path38 = __toESM(require("path"));
28846
-
28847
- // src/core/control-config.ts
28848
- var DEFAULT_CONTROL_API_URL = "http://localhost:3847";
28849
- function getControlApiUrl() {
28850
- return process.env.HAR_CONTROL_API_URL ?? DEFAULT_CONTROL_API_URL;
28851
- }
28852
- function isControlEnabled() {
28853
- return process.env.HAR_CONTROL_DISABLED !== "true";
28854
- }
29248
+ init_control_config();
28855
29249
 
28856
29250
  // src/core/control-registry.ts
28857
29251
  var fs26 = __toESM(require("fs"));
@@ -29097,6 +29491,7 @@ var path30 = __toESM(require("path"));
29097
29491
  var import_child_process4 = require("child_process");
29098
29492
  var fs29 = __toESM(require("fs"));
29099
29493
  var path28 = __toESM(require("path"));
29494
+ init_control_config();
29100
29495
 
29101
29496
  // src/core/slot-ports.ts
29102
29497
  var import_child_process3 = require("child_process");
@@ -29319,7 +29714,6 @@ function collectOccupiedSlot(repoPath, agentId) {
29319
29714
  sessionStatus: session?.status,
29320
29715
  lastError: session?.lastError,
29321
29716
  sessionCreatedAt: session?.createdAt,
29322
- purpose: session?.purpose,
29323
29717
  dirty,
29324
29718
  harnessUsage: "none"
29325
29719
  };
@@ -29330,7 +29724,6 @@ function formatOccupiedSlot(slot, resume) {
29330
29724
  slot.worktreePath ? ` Worktree: ${slot.worktreePath}` : void 0,
29331
29725
  slot.branch ? ` Branch: ${slot.branch}` : void 0,
29332
29726
  slot.workDir ? ` Work dir: ${slot.workDir}` : void 0,
29333
- slot.purpose ? ` Purpose: ${slot.purpose}` : void 0,
29334
29727
  slot.sessionStatus ? ` Status: ${slot.sessionStatus}` : void 0,
29335
29728
  slot.lastError ? ` Error: ${slot.lastError}` : void 0,
29336
29729
  slot.sessionCreatedAt ? ` Since: ${slot.sessionCreatedAt}` : void 0,
@@ -29805,7 +30198,6 @@ function collectSlotStatus(harnessRoot, agentId, runs, pm2Procs) {
29805
30198
  baseBranch: session?.baseBranch,
29806
30199
  baseCommit: session?.baseCommit,
29807
30200
  sessionCreatedAt: session?.createdAt,
29808
- purpose: session?.purpose,
29809
30201
  sessionStatus: session?.status,
29810
30202
  lastError: session?.lastError,
29811
30203
  resumeHint,
@@ -30044,105 +30436,19 @@ function createRemoteExecutor(apiUrl, apiKey) {
30044
30436
  return new RemoteExecutor(url, key);
30045
30437
  }
30046
30438
 
30047
- // src/core/telemetry-config.ts
30048
- var fs34 = __toESM(require("fs"));
30049
- var os9 = __toESM(require("os"));
30050
- var path34 = __toESM(require("path"));
30051
- var DEFAULT_SIGNALS_ON = {
30052
- metrics: true,
30053
- logs: true,
30054
- prompts: false,
30055
- traces: false
30056
- };
30057
- var DEFAULT_SIGNALS_OFF = {
30058
- metrics: false,
30059
- logs: false,
30060
- prompts: false,
30061
- traces: false
30062
- };
30063
- function getPreferencePath() {
30064
- if (process.env.HAR_TELEMETRY_CONFIG_PATH) {
30065
- return path34.resolve(process.env.HAR_TELEMETRY_CONFIG_PATH);
30066
- }
30067
- return path34.join(os9.homedir(), ".har", "telemetry.json");
30068
- }
30069
- function parseEnvOverride(raw) {
30070
- if (raw === void 0 || raw === "") return void 0;
30071
- const normalized = raw.trim().toLowerCase();
30072
- if (["0", "false", "off", "no", "disabled"].includes(normalized)) return false;
30073
- if (["1", "true", "on", "yes", "enabled"].includes(normalized)) return true;
30074
- return void 0;
30075
- }
30076
- function normalizeSignals(enabled, raw) {
30077
- if (!enabled) return { ...DEFAULT_SIGNALS_OFF };
30078
- return {
30079
- metrics: raw?.metrics !== false,
30080
- logs: raw?.logs !== false,
30081
- prompts: raw?.prompts === true,
30082
- traces: raw?.traces === true
30083
- };
30084
- }
30085
- function readTelemetryPreference() {
30086
- const preferencePath = getPreferencePath();
30087
- try {
30088
- if (!fs34.existsSync(preferencePath)) {
30089
- return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
30090
- }
30091
- const parsed = JSON.parse(fs34.readFileSync(preferencePath, "utf8"));
30092
- const enabled = parsed.enabled !== false;
30093
- return {
30094
- enabled,
30095
- signals: normalizeSignals(enabled, parsed.signals),
30096
- updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : void 0
30097
- };
30098
- } catch {
30099
- return { enabled: true, signals: { ...DEFAULT_SIGNALS_ON } };
30100
- }
30101
- }
30102
- function writeTelemetryPreference(enabled, signals) {
30103
- const current = readTelemetryPreference();
30104
- const nextSignals = normalizeSignals(enabled, {
30105
- ...current.signals,
30106
- ...signals
30107
- });
30108
- const preference = {
30109
- enabled,
30110
- signals: enabled ? nextSignals : { ...DEFAULT_SIGNALS_OFF },
30111
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
30112
- };
30113
- const preferencePath = getPreferencePath();
30114
- fs34.mkdirSync(path34.dirname(preferencePath), { recursive: true });
30115
- fs34.writeFileSync(preferencePath, JSON.stringify(preference, null, 2) + "\n");
30116
- return preference;
30117
- }
30118
- function isTelemetryEnabled() {
30119
- const envOverride = parseEnvOverride(process.env.HAR_TELEMETRY);
30120
- if (envOverride !== void 0) return envOverride;
30121
- return readTelemetryPreference().enabled;
30122
- }
30123
- function getTelemetrySignals() {
30124
- if (!isTelemetryEnabled()) return { ...DEFAULT_SIGNALS_OFF };
30125
- return readTelemetryPreference().signals;
30126
- }
30127
- function getTelemetryPreferencePath() {
30128
- return getPreferencePath();
30129
- }
30130
- var TELEMETRY_SIGNALS = [
30131
- "Metrics (default): tokens and estimated USD cost via OTEL metrics",
30132
- "Logs/events (default): session events (tool calls, api_request) without prompt bodies",
30133
- "Prompts (opt-in): user/assistant text via OTEL_LOG_USER_PROMPTS \u2014 har telemetry on --prompts",
30134
- "Traces (opt-in): thin span ingest when CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1",
30135
- "Fallback: har control sync harvests local Claude/Codex session files when OTEL is missing"
30136
- ];
30439
+ // src/core/control-sync.ts
30440
+ init_telemetry_config();
30137
30441
 
30138
30442
  // src/core/usage-harvest/claude.ts
30139
30443
  var fs36 = __toESM(require("fs"));
30140
30444
  var os10 = __toESM(require("os"));
30141
30445
  var path36 = __toESM(require("path"));
30446
+ init_telemetry_config();
30142
30447
 
30143
30448
  // src/core/telemetry-env.ts
30144
30449
  var fs35 = __toESM(require("fs"));
30145
30450
  var path35 = __toESM(require("path"));
30451
+ init_control_config();
30146
30452
  function escapeAttrValue(value) {
30147
30453
  return value.replace(/[,=]/g, "_");
30148
30454
  }
@@ -30155,7 +30461,6 @@ function buildOtelResourceAttributes(attrs) {
30155
30461
  ];
30156
30462
  if (attrs.branch) parts.push(`har.branch=${escapeAttrValue(attrs.branch)}`);
30157
30463
  if (attrs.suffix) parts.push(`har.suffix=${escapeAttrValue(attrs.suffix)}`);
30158
- if (attrs.purpose) parts.push(`har.purpose=${escapeAttrValue(attrs.purpose)}`);
30159
30464
  return parts.join(",");
30160
30465
  }
30161
30466
  function buildSessionKey(input) {
@@ -30163,49 +30468,22 @@ function buildSessionKey(input) {
30163
30468
  const stamp = (input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()).replace(/[:.]/g, "-");
30164
30469
  return `agent-${input.agentId}-${input.suffix ?? stamp}`;
30165
30470
  }
30166
- function buildTelemetryEnvBlock(attrs, options) {
30471
+ function buildTelemetryEnvBlock(attrs) {
30167
30472
  const apiUrl = getControlApiUrl().replace(/\/$/, "");
30168
30473
  const lines = [
30169
30474
  "",
30170
30475
  "# HAR session attribution (generated)",
30171
30476
  `HAR_SESSION_KEY=${attrs.sessionKey}`,
30172
30477
  `HAR_CONTROL_API_URL=${apiUrl}`,
30173
- `OTEL_RESOURCE_ATTRIBUTES=${buildOtelResourceAttributes(attrs)}`
30478
+ `OTEL_RESOURCE_ATTRIBUTES=${buildOtelResourceAttributes(attrs)}`,
30479
+ "# Agent telemetry: opentelemetry-hooks \u2192 Mission Control (har telemetry on|off)"
30174
30480
  ];
30175
- const injectOtel = isTelemetryEnabled() && options?.otelReady !== false;
30176
- if (injectOtel) {
30177
- const signals = getTelemetrySignals();
30178
- lines.push(
30179
- "# HAR telemetry \u2192 Mission Control OTLP (disable: har telemetry off)",
30180
- "CLAUDE_CODE_ENABLE_TELEMETRY=1",
30181
- "OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
30182
- `OTEL_EXPORTER_OTLP_ENDPOINT=${apiUrl}/api/otel`
30183
- );
30184
- if (signals.metrics) {
30185
- lines.push("OTEL_METRICS_EXPORTER=otlp");
30186
- }
30187
- if (signals.logs) {
30188
- lines.push("OTEL_LOGS_EXPORTER=otlp");
30189
- }
30190
- if (signals.prompts) {
30191
- lines.push(
30192
- "OTEL_LOG_USER_PROMPTS=1",
30193
- "OTEL_LOG_ASSISTANT_RESPONSES=1"
30194
- );
30195
- }
30196
- if (signals.traces) {
30197
- lines.push(
30198
- "OTEL_TRACES_EXPORTER=otlp",
30199
- "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1"
30200
- );
30201
- }
30202
- }
30203
30481
  return lines.join("\n") + "\n";
30204
30482
  }
30205
30483
  var TELEMETRY_MARKER_START = "# HAR session attribution (generated)";
30206
30484
  var TELEMETRY_MARKER_END = "# end HAR telemetry";
30207
- function appendTelemetryEnvToFile(envFilePath, attrs, options) {
30208
- const block = buildTelemetryEnvBlock(attrs, options).trimEnd() + `
30485
+ function appendTelemetryEnvToFile(envFilePath, attrs) {
30486
+ const block = buildTelemetryEnvBlock(attrs).trimEnd() + `
30209
30487
  ${TELEMETRY_MARKER_END}
30210
30488
  `;
30211
30489
  let existing = "";
@@ -30225,22 +30503,6 @@ ${TELEMETRY_MARKER_END}
30225
30503
  const combined = (existing.replace(/\n+$/, "\n") + block).replace(/^\n+/, "");
30226
30504
  fs35.writeFileSync(envFilePath, combined.endsWith("\n") ? combined : combined + "\n");
30227
30505
  }
30228
- function buildCodexOtelSnippet(attrs) {
30229
- const apiUrl = getControlApiUrl().replace(/\/$/, "");
30230
- const resource = buildOtelResourceAttributes(attrs);
30231
- return `# Merge into ~/.codex/config.toml (HAR does not overwrite your config)
30232
- # Generated for session ${attrs.sessionKey}
30233
-
30234
- [otel]
30235
- exporter = "otlp-http"
30236
- endpoint = "${apiUrl}/api/otel"
30237
- metrics_exporter = "otlp-http"
30238
-
30239
- # Optional: set resource attributes via environment when starting Codex:
30240
- # export OTEL_RESOURCE_ATTRIBUTES="${resource}"
30241
- # export HAR_SESSION_KEY="${attrs.sessionKey}"
30242
- `;
30243
- }
30244
30506
 
30245
30507
  // src/core/usage-harvest/claude.ts
30246
30508
  function pathsMatch(candidate, targets) {
@@ -30578,7 +30840,7 @@ async function waitForControlApi(apiUrl = getControlApiUrl(), timeoutMs = 6e4, i
30578
30840
  const deadline = Date.now() + timeoutMs;
30579
30841
  while (Date.now() < deadline) {
30580
30842
  if (await isControlApiReachable(apiUrl)) return true;
30581
- await new Promise((resolve33) => setTimeout(resolve33, intervalMs));
30843
+ await new Promise((resolve34) => setTimeout(resolve34, intervalMs));
30582
30844
  }
30583
30845
  return false;
30584
30846
  }
@@ -30775,6 +31037,9 @@ function checkLaunchGuard2(repoPath, agentId, options = {}) {
30775
31037
  return { allowed: true, slot: occupied.slot, readiness };
30776
31038
  }
30777
31039
 
31040
+ // src/core/telemetry-ensure.ts
31041
+ init_control_config();
31042
+
30778
31043
  // src/core/control-lifecycle.ts
30779
31044
  var import_child_process8 = require("child_process");
30780
31045
  var path39 = __toESM(require("path"));
@@ -30795,6 +31060,7 @@ function shouldBuildControlLocally() {
30795
31060
  }
30796
31061
 
30797
31062
  // src/core/control-lifecycle.ts
31063
+ init_control_config();
30798
31064
  var CONTROL_CONTAINER_NAME = "har-control";
30799
31065
  var CONTROL_DATA_VOLUME = "har_control_data";
30800
31066
  var CONTROL_CONTAINER_PORT = 3847;
@@ -30903,6 +31169,7 @@ async function startControlAndSync(options) {
30903
31169
  }
30904
31170
 
30905
31171
  // src/core/telemetry-ensure.ts
31172
+ init_telemetry_config();
30906
31173
  async function ensureTelemetryInfrastructure(options) {
30907
31174
  const apiUrl = getControlApiUrl();
30908
31175
  const telemetryEnabled = isTelemetryEnabled();
@@ -31001,7 +31268,8 @@ async function ensureTelemetryInfrastructure(options) {
31001
31268
  }
31002
31269
 
31003
31270
  // src/core/run-service.ts
31004
- var path40 = __toESM(require("path"));
31271
+ init_telemetry_config();
31272
+ var path41 = __toESM(require("path"));
31005
31273
  function extractShellOutput(result) {
31006
31274
  const stdout = result.logs?.find((log) => log.stream === "stdout")?.content ?? result.logs?.find((log) => !log.stream)?.content ?? "";
31007
31275
  const stderr = result.logs?.find((log) => log.stream === "stderr")?.content ?? "";
@@ -31117,23 +31385,31 @@ var RunService = class {
31117
31385
  worktreePath: slot.worktreePath,
31118
31386
  branch: slot.branch,
31119
31387
  dirty: slot.dirty,
31120
- sessionCreatedAt: slot.sessionCreatedAt,
31121
- purpose: slot.purpose
31388
+ sessionCreatedAt: slot.sessionCreatedAt
31122
31389
  } : void 0
31123
31390
  };
31124
31391
  }
31125
31392
  let telemetryBanner = "";
31126
- let otelReady = false;
31127
31393
  if (isTelemetryEnabled() && process.env.NODE_ENV !== "test") {
31128
31394
  const ensured = await ensureTelemetryInfrastructure({ startIfNeeded: true });
31129
- otelReady = ensured.otelReady;
31130
31395
  if (ensured.message) {
31131
31396
  telemetryBanner += `${ensured.message}
31132
- Usage from Claude Code / Codex will appear under Worktrees. Disable: har telemetry off
31397
+ Usage from Cursor / Claude / Codex (opentelemetry-hooks) will appear under Worktrees. Disable: har telemetry off
31133
31398
  `;
31134
31399
  }
31135
31400
  if (ensured.warning) {
31136
31401
  telemetryBanner += `${ensured.warning}
31402
+ `;
31403
+ }
31404
+ try {
31405
+ const { ensureOtelHooks: ensureOtelHooks2 } = await Promise.resolve().then(() => (init_otel_hooks(), otel_hooks_exports));
31406
+ const hooks = ensureOtelHooks2({ setupAgents: true });
31407
+ if (hooks.message) telemetryBanner += `${hooks.message}
31408
+ `;
31409
+ if (hooks.warning) telemetryBanner += `${hooks.warning}
31410
+ `;
31411
+ } catch (err) {
31412
+ telemetryBanner += `opentelemetry-hooks setup skipped: ${err instanceof Error ? err.message : String(err)}
31137
31413
  `;
31138
31414
  }
31139
31415
  }
@@ -31147,8 +31423,7 @@ Usage from Claude Code / Codex will appear under Worktrees. Disable: har telemet
31147
31423
  claude: options.claude,
31148
31424
  confirmReplace: options.confirmReplace,
31149
31425
  force: options.force,
31150
- resume: options.resume,
31151
- purpose: options.purpose
31426
+ resume: options.resume
31152
31427
  },
31153
31428
  trigger: "cli"
31154
31429
  });
@@ -31167,21 +31442,16 @@ Usage from Claude Code / Codex will appear under Worktrees. Disable: har telemet
31167
31442
  suffix: session.suffix,
31168
31443
  createdAt: session.createdAt
31169
31444
  });
31170
- const envFile = path40.join(session.workDir, `.env.agent.${options.agentId}`);
31445
+ const envFile = path41.join(session.workDir, `.env.agent.${options.agentId}`);
31171
31446
  try {
31172
- appendTelemetryEnvToFile(
31173
- envFile,
31174
- {
31175
- sessionKey,
31176
- agentId: options.agentId,
31177
- repoPath: path40.resolve(options.repoPath),
31178
- workDir: session.workDir,
31179
- branch: session.branch,
31180
- suffix: session.suffix,
31181
- purpose: session.purpose
31182
- },
31183
- { otelReady: otelReady && isTelemetryEnabled() }
31184
- );
31447
+ appendTelemetryEnvToFile(envFile, {
31448
+ sessionKey,
31449
+ agentId: options.agentId,
31450
+ repoPath: path41.resolve(options.repoPath),
31451
+ workDir: session.workDir,
31452
+ branch: session.branch,
31453
+ suffix: session.suffix
31454
+ });
31185
31455
  } catch {
31186
31456
  }
31187
31457
  }
@@ -31467,9 +31737,6 @@ var envCommand = {
31467
31737
  type: "boolean",
31468
31738
  default: false,
31469
31739
  describe: "Resume a failed or partial launch without creating a new worktree"
31470
- }).option("purpose", {
31471
- type: "string",
31472
- describe: "Optional session label stored in the slot registry (shown in status and occupied-slot warnings)"
31473
31740
  }),
31474
31741
  handleLaunch
31475
31742
  ).command(
@@ -31543,7 +31810,7 @@ var envCommand = {
31543
31810
  }
31544
31811
  };
31545
31812
  async function handleInit(argv) {
31546
- const repoPath = path41.resolve(argv.repo);
31813
+ const repoPath = path42.resolve(argv.repo);
31547
31814
  header("har env init");
31548
31815
  info(`Repository: ${repoPath}`);
31549
31816
  try {
@@ -31601,7 +31868,7 @@ async function handleInit(argv) {
31601
31868
  }
31602
31869
  }
31603
31870
  async function handleMaintain(argv) {
31604
- const repoPath = path41.resolve(argv.repo);
31871
+ const repoPath = path42.resolve(argv.repo);
31605
31872
  header("har env maintain");
31606
31873
  info(`Repository: ${repoPath}`);
31607
31874
  try {
@@ -31699,7 +31966,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
31699
31966
  if (autoYes) {
31700
31967
  const proposal = readAgentMdProposal(repoPath);
31701
31968
  if (proposal) {
31702
- writeFileSafe(path41.join(repoPath, "AGENT.md"), proposal.content);
31969
+ writeFileSafe(path42.join(repoPath, "AGENT.md"), proposal.content);
31703
31970
  clearAgentMdProposal(repoPath);
31704
31971
  info("Applied AGENT.md proposal (--yes)");
31705
31972
  }
@@ -31708,7 +31975,7 @@ async function handleAgentMdProposal(repoPath, autoYes) {
31708
31975
  await promptApplyAgentMdProposal(repoPath);
31709
31976
  }
31710
31977
  async function handleAddStage(argv) {
31711
- const repoPath = path41.resolve(argv.repo);
31978
+ const repoPath = path42.resolve(argv.repo);
31712
31979
  const available = listStageTemplateIds();
31713
31980
  if (argv.list) {
31714
31981
  for (const id of available) {
@@ -31785,7 +32052,7 @@ async function handleAddStage(argv) {
31785
32052
  }
31786
32053
  }
31787
32054
  async function handlePreflight(argv) {
31788
- const repo = path41.resolve(argv.repo);
32055
+ const repo = path42.resolve(argv.repo);
31789
32056
  const agentId = validateAgentId(argv.id, repo);
31790
32057
  const result = await preflightEnvironment({
31791
32058
  repoPath: repo,
@@ -31803,7 +32070,7 @@ async function handlePreflight(argv) {
31803
32070
  process.exit(result.code);
31804
32071
  }
31805
32072
  async function handleLaunch(argv) {
31806
- const repo = path41.resolve(argv.repo);
32073
+ const repo = path42.resolve(argv.repo);
31807
32074
  const agentId = validateAgentId(argv.id, repo);
31808
32075
  let confirmReplace = argv.replace;
31809
32076
  let force = argv.force;
@@ -31815,8 +32082,8 @@ async function handleLaunch(argv) {
31815
32082
  console.error(guard.reason ?? "");
31816
32083
  const readline5 = await import("readline");
31817
32084
  const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
31818
- const answer = await new Promise((resolve33) => {
31819
- rl.question("Replace this slot? [y/N] ", resolve33);
32085
+ const answer = await new Promise((resolve34) => {
32086
+ rl.question("Replace this slot? [y/N] ", resolve34);
31820
32087
  });
31821
32088
  rl.close();
31822
32089
  if (!/^[Yy]$/.test(answer.trim())) {
@@ -31832,8 +32099,8 @@ async function handleLaunch(argv) {
31832
32099
  warn("Worktree has uncommitted changes.");
31833
32100
  const readline5 = await import("readline");
31834
32101
  const rl = readline5.createInterface({ input: process.stdin, output: process.stderr });
31835
- const answer = await new Promise((resolve33) => {
31836
- rl.question("Discard uncommitted changes? [y/N] ", resolve33);
32102
+ const answer = await new Promise((resolve34) => {
32103
+ rl.question("Discard uncommitted changes? [y/N] ", resolve34);
31837
32104
  });
31838
32105
  rl.close();
31839
32106
  if (!/^[Yy]$/.test(answer.trim())) {
@@ -31855,7 +32122,6 @@ async function handleLaunch(argv) {
31855
32122
  confirmReplace,
31856
32123
  force,
31857
32124
  resume: argv.resume,
31858
- purpose: argv.purpose,
31859
32125
  capture: false
31860
32126
  });
31861
32127
  if (result.blocked) {
@@ -31888,7 +32154,7 @@ async function handleRecover(argv) {
31888
32154
  });
31889
32155
  }
31890
32156
  async function handleVerify(argv) {
31891
- const repo = path41.resolve(argv.repo);
32157
+ const repo = path42.resolve(argv.repo);
31892
32158
  const agentId = validateAgentId(argv.id, repo);
31893
32159
  const result = await runVerification({
31894
32160
  repoPath: repo,
@@ -31900,7 +32166,7 @@ async function handleVerify(argv) {
31900
32166
  process.exit(result.code);
31901
32167
  }
31902
32168
  async function handleTeardown(argv) {
31903
- const repo = path41.resolve(argv.repo);
32169
+ const repo = path42.resolve(argv.repo);
31904
32170
  const agentId = validateAgentId(argv.id, repo);
31905
32171
  const result = await teardownEnvironment({
31906
32172
  repoPath: repo,
@@ -31911,7 +32177,7 @@ async function handleTeardown(argv) {
31911
32177
  process.exit(result.code);
31912
32178
  }
31913
32179
  async function handleComplete(argv) {
31914
- const repo = path41.resolve(argv.repo);
32180
+ const repo = path42.resolve(argv.repo);
31915
32181
  const agentId = validateAgentId(argv.id, repo);
31916
32182
  const result = await completeEnvironment({
31917
32183
  repoPath: repo,
@@ -31932,7 +32198,7 @@ async function handleComplete(argv) {
31932
32198
  process.exit(result.code);
31933
32199
  }
31934
32200
  async function handleStatus(argv) {
31935
- const repoPath = path41.resolve(argv.repo);
32201
+ const repoPath = path42.resolve(argv.repo);
31936
32202
  if (argv.json) {
31937
32203
  const status = collectEnvironmentStatus(repoPath);
31938
32204
  const output = EnvironmentStatusSchema.parse(status);
@@ -31945,7 +32211,7 @@ async function handleStatus(argv) {
31945
32211
  });
31946
32212
  }
31947
32213
  async function handleRunsList(argv) {
31948
- const repoPath = path41.resolve(argv.repo);
32214
+ const repoPath = path42.resolve(argv.repo);
31949
32215
  const runs = listRuns(repoPath, { stageId: argv.stage, limit: argv.limit });
31950
32216
  if (argv.json) {
31951
32217
  process.stdout.write(JSON.stringify({ runs }, null, 2) + "\n");
@@ -31962,7 +32228,7 @@ async function handleRunsList(argv) {
31962
32228
  }
31963
32229
  }
31964
32230
  async function handleRunsGet(argv) {
31965
- const run2 = getRun(path41.resolve(argv.repo), argv.runId);
32231
+ const run2 = getRun(path42.resolve(argv.repo), argv.runId);
31966
32232
  if (!run2) {
31967
32233
  error(`Run not found: ${argv.runId}`);
31968
32234
  process.exit(1);
@@ -32041,7 +32307,8 @@ function printNextSteps(auto2) {
32041
32307
  }
32042
32308
 
32043
32309
  // src/cli/commands/control.ts
32044
- var path42 = __toESM(require("path"));
32310
+ var path43 = __toESM(require("path"));
32311
+ init_control_config();
32045
32312
  var controlCommand = {
32046
32313
  command: "control <subcommand>",
32047
32314
  describe: "Mission Control dashboard (local)",
@@ -32144,7 +32411,7 @@ async function handleDown() {
32144
32411
  process.exit(code);
32145
32412
  }
32146
32413
  async function handleRegister(argv) {
32147
- const repoPath = path42.resolve(argv.repo);
32414
+ const repoPath = path43.resolve(argv.repo);
32148
32415
  header("har control register");
32149
32416
  info(`Repository: ${repoPath}`);
32150
32417
  try {
@@ -32164,7 +32431,7 @@ async function handleRegister(argv) {
32164
32431
  }
32165
32432
  }
32166
32433
  async function handleSync(argv) {
32167
- const repoPath = path42.resolve(argv.repo);
32434
+ const repoPath = path43.resolve(argv.repo);
32168
32435
  try {
32169
32436
  await syncRepoWithControl({
32170
32437
  repoPath,
@@ -32203,7 +32470,7 @@ async function handleWatch(argv) {
32203
32470
  };
32204
32471
  const tick = async () => {
32205
32472
  if (argv.repo) {
32206
- await syncOne(path42.resolve(argv.repo));
32473
+ await syncOne(path43.resolve(argv.repo));
32207
32474
  return;
32208
32475
  }
32209
32476
  try {
@@ -32234,12 +32501,12 @@ async function handleLogin(argv) {
32234
32501
  }
32235
32502
 
32236
32503
  // src/cli/commands/hooks.ts
32237
- var path45 = __toESM(require("path"));
32504
+ var path46 = __toESM(require("path"));
32238
32505
 
32239
32506
  // src/core/hooks.ts
32240
- var fs38 = __toESM(require("fs"));
32241
- var os12 = __toESM(require("os"));
32242
- var path43 = __toESM(require("path"));
32507
+ var fs39 = __toESM(require("fs"));
32508
+ var os13 = __toESM(require("os"));
32509
+ var path44 = __toESM(require("path"));
32243
32510
  var MARKER_START = "# >>> har commit gate (managed by `har hooks`) >>>";
32244
32511
  var MARKER_END = "# <<< har commit gate <<<";
32245
32512
  var PRE_COMMIT_BLOCK = [
@@ -32264,7 +32531,7 @@ function resolveCheckoutRoot(cwd) {
32264
32531
  function resolveHooksDir(checkoutDir) {
32265
32532
  const hooksPath = tryGit2(checkoutDir, "rev-parse --git-path hooks");
32266
32533
  if (!hooksPath) throw new Error(`Not a git checkout: ${checkoutDir}`);
32267
- return path43.resolve(checkoutDir, hooksPath);
32534
+ return path44.resolve(checkoutDir, hooksPath);
32268
32535
  }
32269
32536
  function getConfiguredHooksPath(checkoutDir) {
32270
32537
  return tryGit2(checkoutDir, "config core.hooksPath") || void 0;
@@ -32272,9 +32539,9 @@ function getConfiguredHooksPath(checkoutDir) {
32272
32539
  function defaultHarInvocation() {
32273
32540
  const entry = process.argv[1];
32274
32541
  if (entry && entry.endsWith(".js")) {
32275
- return `"${process.execPath}" "${path43.resolve(entry)}"`;
32542
+ return `"${process.execPath}" "${path44.resolve(entry)}"`;
32276
32543
  }
32277
- if (entry) return `"${path43.resolve(entry)}"`;
32544
+ if (entry) return `"${path44.resolve(entry)}"`;
32278
32545
  return "har";
32279
32546
  }
32280
32547
  function buildHookScript(harInvocation, subcommand, failOpenNotice) {
@@ -32295,40 +32562,40 @@ exit 0
32295
32562
  `;
32296
32563
  }
32297
32564
  function upsertMarkedBlock(filePath, block) {
32298
- if (!fs38.existsSync(filePath)) {
32299
- fs38.writeFileSync(filePath, `#!/bin/sh
32565
+ if (!fs39.existsSync(filePath)) {
32566
+ fs39.writeFileSync(filePath, `#!/bin/sh
32300
32567
  ${block}
32301
32568
  `, { mode: 493 });
32302
32569
  return "created";
32303
32570
  }
32304
- const content = fs38.readFileSync(filePath, "utf8");
32571
+ const content = fs39.readFileSync(filePath, "utf8");
32305
32572
  if (content.includes(MARKER_START)) {
32306
32573
  const pattern = new RegExp(
32307
32574
  `${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}`
32308
32575
  );
32309
- fs38.writeFileSync(filePath, content.replace(pattern, block));
32310
- fs38.chmodSync(filePath, 493);
32576
+ fs39.writeFileSync(filePath, content.replace(pattern, block));
32577
+ fs39.chmodSync(filePath, 493);
32311
32578
  return "updated";
32312
32579
  }
32313
32580
  const suffix = content.endsWith("\n") ? "" : "\n";
32314
- fs38.writeFileSync(filePath, `${content}${suffix}
32581
+ fs39.writeFileSync(filePath, `${content}${suffix}
32315
32582
  ${block}
32316
32583
  `);
32317
- fs38.chmodSync(filePath, 493);
32584
+ fs39.chmodSync(filePath, 493);
32318
32585
  return "appended";
32319
32586
  }
32320
32587
  function removeMarkedBlock(filePath) {
32321
- if (!fs38.existsSync(filePath)) return false;
32322
- const content = fs38.readFileSync(filePath, "utf8");
32588
+ if (!fs39.existsSync(filePath)) return false;
32589
+ const content = fs39.readFileSync(filePath, "utf8");
32323
32590
  if (!content.includes(MARKER_START)) return false;
32324
32591
  const pattern = new RegExp(
32325
32592
  `\\n?${escapeRegExp(MARKER_START)}[\\s\\S]*?${escapeRegExp(MARKER_END)}\\n?`
32326
32593
  );
32327
32594
  const stripped = content.replace(pattern, "\n");
32328
32595
  if (stripped.replace(/^#!\/bin\/sh\n?/, "").trim() === "") {
32329
- fs38.rmSync(filePath);
32596
+ fs39.rmSync(filePath);
32330
32597
  } else {
32331
- fs38.writeFileSync(filePath, stripped);
32598
+ fs39.writeFileSync(filePath, stripped);
32332
32599
  }
32333
32600
  return true;
32334
32601
  }
@@ -32350,20 +32617,20 @@ Or re-run with --force to write into that directory anyway.`
32350
32617
  );
32351
32618
  }
32352
32619
  const hooksDir = resolveHooksDir(checkout);
32353
- fs38.mkdirSync(hooksDir, { recursive: true });
32620
+ fs39.mkdirSync(hooksDir, { recursive: true });
32354
32621
  const invocation = options.harInvocation ?? defaultHarInvocation();
32355
- fs38.writeFileSync(
32356
- path43.join(hooksDir, "har-pre-commit"),
32622
+ fs39.writeFileSync(
32623
+ path44.join(hooksDir, "har-pre-commit"),
32357
32624
  buildHookScript(invocation, "check", "har: binary not found; skipping commit gate (reinstall with har hooks install)"),
32358
32625
  { mode: 493 }
32359
32626
  );
32360
- fs38.writeFileSync(
32361
- path43.join(hooksDir, "har-post-commit"),
32627
+ fs39.writeFileSync(
32628
+ path44.join(hooksDir, "har-post-commit"),
32362
32629
  buildHookScript(invocation, "record-commit", "har: binary not found; skipping commit association"),
32363
32630
  { mode: 493 }
32364
32631
  );
32365
- const preCommit = upsertMarkedBlock(path43.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
32366
- const postCommit = upsertMarkedBlock(path43.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
32632
+ const preCommit = upsertMarkedBlock(path44.join(hooksDir, "pre-commit"), PRE_COMMIT_BLOCK);
32633
+ const postCommit = upsertMarkedBlock(path44.join(hooksDir, "post-commit"), POST_COMMIT_BLOCK);
32367
32634
  ensureValidationsIgnored(checkout);
32368
32635
  return { hooksDir, preCommit, postCommit };
32369
32636
  }
@@ -32372,12 +32639,12 @@ function uninstallHooks(repoPath) {
32372
32639
  if (!checkout) throw new Error(`Not a git repository: ${repoPath}`);
32373
32640
  const hooksDir = resolveHooksDir(checkout);
32374
32641
  let removed = false;
32375
- removed = removeMarkedBlock(path43.join(hooksDir, "pre-commit")) || removed;
32376
- removed = removeMarkedBlock(path43.join(hooksDir, "post-commit")) || removed;
32642
+ removed = removeMarkedBlock(path44.join(hooksDir, "pre-commit")) || removed;
32643
+ removed = removeMarkedBlock(path44.join(hooksDir, "post-commit")) || removed;
32377
32644
  for (const file of ["har-pre-commit", "har-post-commit"]) {
32378
- const full = path43.join(hooksDir, file);
32379
- if (fs38.existsSync(full)) {
32380
- fs38.rmSync(full);
32645
+ const full = path44.join(hooksDir, file);
32646
+ if (fs39.existsSync(full)) {
32647
+ fs39.rmSync(full);
32381
32648
  removed = true;
32382
32649
  }
32383
32650
  }
@@ -32394,9 +32661,9 @@ function getCommitGateConfig(checkoutDir) {
32394
32661
  function isAgentWorktree(checkoutDir) {
32395
32662
  const branch = getCurrentBranch(checkoutDir);
32396
32663
  if (branch && /^har-agent-\d+$/.test(branch)) return true;
32397
- const worktreesRoot = path43.join(os12.homedir(), "worktrees");
32398
- const resolved = path43.resolve(checkoutDir);
32399
- return resolved.startsWith(`${worktreesRoot}${path43.sep}`) && /-agent-\d+$/.test(path43.basename(resolved));
32664
+ const worktreesRoot = path44.join(os13.homedir(), "worktrees");
32665
+ const resolved = path44.resolve(checkoutDir);
32666
+ return resolved.startsWith(`${worktreesRoot}${path44.sep}`) && /-agent-\d+$/.test(path44.basename(resolved));
32400
32667
  }
32401
32668
  function resolveEffectiveMode(checkoutDir, gate) {
32402
32669
  if (!gate.enabled) return "off";
@@ -32409,15 +32676,15 @@ function getHooksStatus(repoPath) {
32409
32676
  const hooksDir = resolveHooksDir(checkout);
32410
32677
  const gate = getCommitGateConfig(checkout);
32411
32678
  const hasBlock = (name) => {
32412
- const file = path43.join(hooksDir, name);
32413
- return fs38.existsSync(file) && fs38.readFileSync(file, "utf8").includes(MARKER_START);
32679
+ const file = path44.join(hooksDir, name);
32680
+ return fs39.existsSync(file) && fs39.readFileSync(file, "utf8").includes(MARKER_START);
32414
32681
  };
32415
32682
  return {
32416
32683
  checkout,
32417
32684
  hooksDir,
32418
32685
  configuredHooksPath: getConfiguredHooksPath(checkout),
32419
- preCommitInstalled: hasBlock("pre-commit") && fs38.existsSync(path43.join(hooksDir, "har-pre-commit")),
32420
- postCommitInstalled: hasBlock("post-commit") && fs38.existsSync(path43.join(hooksDir, "har-post-commit")),
32686
+ preCommitInstalled: hasBlock("pre-commit") && fs39.existsSync(path44.join(hooksDir, "har-pre-commit")),
32687
+ postCommitInstalled: hasBlock("post-commit") && fs39.existsSync(path44.join(hooksDir, "har-post-commit")),
32421
32688
  gate,
32422
32689
  effectiveMode: resolveEffectiveMode(checkout, gate)
32423
32690
  };
@@ -32428,7 +32695,7 @@ function checkCommitGate(cwd) {
32428
32695
  }
32429
32696
  const checkout = resolveCheckoutRoot(cwd);
32430
32697
  if (!checkout) return { exitCode: 0, messages: [] };
32431
- if (!fs38.existsSync(path43.join(checkout, ".har", "stages.json"))) {
32698
+ if (!fs39.existsSync(path44.join(checkout, ".har", "stages.json"))) {
32432
32699
  return { exitCode: 0, messages: [] };
32433
32700
  }
32434
32701
  const gate = getCommitGateConfig(checkout);
@@ -32499,8 +32766,8 @@ async function recordCommitAssociation(cwd) {
32499
32766
  }
32500
32767
 
32501
32768
  // src/core/claude-hooks.ts
32502
- var fs39 = __toESM(require("fs"));
32503
- var path44 = __toESM(require("path"));
32769
+ var fs40 = __toESM(require("fs"));
32770
+ var path45 = __toESM(require("path"));
32504
32771
  var CLAUDE_GUARD_RELATIVE_PATH = ".har/hooks/claude-worktree-guard.sh";
32505
32772
  var CLAUDE_SETTINGS_RELATIVE_PATH = ".claude/settings.json";
32506
32773
  var GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
@@ -32511,8 +32778,8 @@ function isHarGuardEntry(entry) {
32511
32778
  );
32512
32779
  }
32513
32780
  function readSettings(settingsPath) {
32514
- if (!fs39.existsSync(settingsPath)) return {};
32515
- const raw = fs39.readFileSync(settingsPath, "utf8").trim();
32781
+ if (!fs40.existsSync(settingsPath)) return {};
32782
+ const raw = fs40.readFileSync(settingsPath, "utf8").trim();
32516
32783
  if (raw === "") return {};
32517
32784
  const parsed = JSON.parse(raw);
32518
32785
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -32521,19 +32788,19 @@ function readSettings(settingsPath) {
32521
32788
  return parsed;
32522
32789
  }
32523
32790
  function writeSettings(settingsPath, settings) {
32524
- fs39.mkdirSync(path44.dirname(settingsPath), { recursive: true });
32525
- fs39.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
32791
+ fs40.mkdirSync(path45.dirname(settingsPath), { recursive: true });
32792
+ fs40.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
32526
32793
  }
32527
32794
  function installClaudeGuard(repoPath) {
32528
32795
  const templatePath = resolveTemplateFile("claude-worktree-guard.sh.template");
32529
32796
  if (!templatePath) {
32530
32797
  throw new Error("Guard template not found (claude-worktree-guard.sh.template). Run npm run build.");
32531
32798
  }
32532
- const guardScript = path44.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
32533
- fs39.mkdirSync(path44.dirname(guardScript), { recursive: true });
32534
- fs39.copyFileSync(templatePath, guardScript);
32535
- fs39.chmodSync(guardScript, 493);
32536
- const settingsPath = path44.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
32799
+ const guardScript = path45.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
32800
+ fs40.mkdirSync(path45.dirname(guardScript), { recursive: true });
32801
+ fs40.copyFileSync(templatePath, guardScript);
32802
+ fs40.chmodSync(guardScript, 493);
32803
+ const settingsPath = path45.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
32537
32804
  const settings = readSettings(settingsPath);
32538
32805
  const hooks = settings.hooks ?? {};
32539
32806
  const preToolUse = (hooks.PreToolUse ?? []).filter((entry) => !isHarGuardEntry(entry));
@@ -32547,15 +32814,15 @@ function installClaudeGuard(repoPath) {
32547
32814
  }
32548
32815
  function uninstallClaudeGuard(repoPath) {
32549
32816
  let removed = false;
32550
- const guardScript = path44.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
32551
- if (fs39.existsSync(guardScript)) {
32552
- fs39.rmSync(guardScript);
32817
+ const guardScript = path45.join(repoPath, CLAUDE_GUARD_RELATIVE_PATH);
32818
+ if (fs40.existsSync(guardScript)) {
32819
+ fs40.rmSync(guardScript);
32553
32820
  removed = true;
32554
- const parent = path44.dirname(guardScript);
32555
- if (fs39.existsSync(parent) && fs39.readdirSync(parent).length === 0) fs39.rmdirSync(parent);
32821
+ const parent = path45.dirname(guardScript);
32822
+ if (fs40.existsSync(parent) && fs40.readdirSync(parent).length === 0) fs40.rmdirSync(parent);
32556
32823
  }
32557
- const settingsPath = path44.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
32558
- if (fs39.existsSync(settingsPath)) {
32824
+ const settingsPath = path45.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
32825
+ if (fs40.existsSync(settingsPath)) {
32559
32826
  const settings = readSettings(settingsPath);
32560
32827
  const preToolUse = settings.hooks?.PreToolUse;
32561
32828
  if (preToolUse) {
@@ -32580,8 +32847,8 @@ function uninstallClaudeGuard(repoPath) {
32580
32847
  return { removed };
32581
32848
  }
32582
32849
  function claudeGuardInstalled(repoPath) {
32583
- const settingsPath = path44.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
32584
- if (!fs39.existsSync(settingsPath)) return false;
32850
+ const settingsPath = path45.join(repoPath, CLAUDE_SETTINGS_RELATIVE_PATH);
32851
+ if (!fs40.existsSync(settingsPath)) return false;
32585
32852
  try {
32586
32853
  const settings = readSettings(settingsPath);
32587
32854
  return (settings.hooks?.PreToolUse ?? []).some(isHarGuardEntry);
@@ -32597,7 +32864,7 @@ function repoOption(y2) {
32597
32864
  function handleInstall2(argv) {
32598
32865
  try {
32599
32866
  if (argv.claude) {
32600
- const result2 = installClaudeGuard(path45.resolve(argv.repo));
32867
+ const result2 = installClaudeGuard(path46.resolve(argv.repo));
32601
32868
  success("Claude Code worktree guard installed.");
32602
32869
  info(`Guard script: ${result2.guardScript}`);
32603
32870
  info(`Settings: ${result2.settingsPath} (PreToolUse: Edit|Write|MultiEdit|NotebookEdit)`);
@@ -32605,7 +32872,7 @@ function handleInstall2(argv) {
32605
32872
  info("Commit both files so the guard travels with the repo.");
32606
32873
  return;
32607
32874
  }
32608
- const result = installHooks({ repoPath: path45.resolve(argv.repo), force: argv.force });
32875
+ const result = installHooks({ repoPath: path46.resolve(argv.repo), force: argv.force });
32609
32876
  success(`Commit gate installed in ${result.hooksDir}`);
32610
32877
  info(`pre-commit: ${result.preCommit}, post-commit: ${result.postCommit}`);
32611
32878
  info("Commits of unverified change batches will be blocked in agent worktrees.");
@@ -32618,7 +32885,7 @@ function handleInstall2(argv) {
32618
32885
  function handleUninstall(argv) {
32619
32886
  try {
32620
32887
  if (argv.claude) {
32621
- const result2 = uninstallClaudeGuard(path45.resolve(argv.repo));
32888
+ const result2 = uninstallClaudeGuard(path46.resolve(argv.repo));
32622
32889
  if (result2.removed) {
32623
32890
  success("Claude Code worktree guard removed.");
32624
32891
  } else {
@@ -32626,7 +32893,7 @@ function handleUninstall(argv) {
32626
32893
  }
32627
32894
  return;
32628
32895
  }
32629
- const result = uninstallHooks(path45.resolve(argv.repo));
32896
+ const result = uninstallHooks(path46.resolve(argv.repo));
32630
32897
  if (result.removed) {
32631
32898
  success(`Commit gate removed from ${result.hooksDir}`);
32632
32899
  } else {
@@ -32639,7 +32906,7 @@ function handleUninstall(argv) {
32639
32906
  }
32640
32907
  function handleStatus2(argv) {
32641
32908
  try {
32642
- const status = getHooksStatus(path45.resolve(argv.repo));
32909
+ const status = getHooksStatus(path46.resolve(argv.repo));
32643
32910
  if (argv.json) {
32644
32911
  process.stdout.write(`${JSON.stringify(status, null, 2)}
32645
32912
  `);
@@ -32657,7 +32924,7 @@ function handleStatus2(argv) {
32657
32924
  );
32658
32925
  info(`Effective: ${status.effectiveMode} (in this checkout)`);
32659
32926
  info(
32660
- `Claude guard: ${claudeGuardInstalled(path45.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
32927
+ `Claude guard: ${claudeGuardInstalled(path46.resolve(argv.repo)) ? `installed (${CLAUDE_GUARD_RELATIVE_PATH})` : "not installed"}`
32661
32928
  );
32662
32929
  } catch (err) {
32663
32930
  error(err instanceof Error ? err.message : String(err));
@@ -32918,10 +33185,10 @@ function assignProp(target, prop, value) {
32918
33185
  configurable: true
32919
33186
  });
32920
33187
  }
32921
- function getElementAtPath(obj, path48) {
32922
- if (!path48)
33188
+ function getElementAtPath(obj, path49) {
33189
+ if (!path49)
32923
33190
  return obj;
32924
- return path48.reduce((acc, key) => acc?.[key], obj);
33191
+ return path49.reduce((acc, key) => acc?.[key], obj);
32925
33192
  }
32926
33193
  function promiseAllObject(promisesObj) {
32927
33194
  const keys = Object.keys(promisesObj);
@@ -33241,11 +33508,11 @@ function aborted(x2, startIndex = 0) {
33241
33508
  }
33242
33509
  return false;
33243
33510
  }
33244
- function prefixIssues(path48, issues) {
33511
+ function prefixIssues(path49, issues) {
33245
33512
  return issues.map((iss) => {
33246
33513
  var _a3;
33247
33514
  (_a3 = iss).path ?? (_a3.path = []);
33248
- iss.path.unshift(path48);
33515
+ iss.path.unshift(path49);
33249
33516
  return iss;
33250
33517
  });
33251
33518
  }
@@ -38568,7 +38835,7 @@ var Protocol = class {
38568
38835
  return;
38569
38836
  }
38570
38837
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
38571
- await new Promise((resolve33) => setTimeout(resolve33, pollInterval));
38838
+ await new Promise((resolve34) => setTimeout(resolve34, pollInterval));
38572
38839
  options?.signal?.throwIfAborted();
38573
38840
  }
38574
38841
  } catch (error3) {
@@ -38585,7 +38852,7 @@ var Protocol = class {
38585
38852
  */
38586
38853
  request(request, resultSchema, options) {
38587
38854
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
38588
- return new Promise((resolve33, reject) => {
38855
+ return new Promise((resolve34, reject) => {
38589
38856
  const earlyReject = (error3) => {
38590
38857
  reject(error3);
38591
38858
  };
@@ -38663,7 +38930,7 @@ var Protocol = class {
38663
38930
  if (!parseResult.success) {
38664
38931
  reject(parseResult.error);
38665
38932
  } else {
38666
- resolve33(parseResult.data);
38933
+ resolve34(parseResult.data);
38667
38934
  }
38668
38935
  } catch (error3) {
38669
38936
  reject(error3);
@@ -38924,12 +39191,12 @@ var Protocol = class {
38924
39191
  }
38925
39192
  } catch {
38926
39193
  }
38927
- return new Promise((resolve33, reject) => {
39194
+ return new Promise((resolve34, reject) => {
38928
39195
  if (signal.aborted) {
38929
39196
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
38930
39197
  return;
38931
39198
  }
38932
- const timeoutId = setTimeout(resolve33, interval);
39199
+ const timeoutId = setTimeout(resolve34, interval);
38933
39200
  signal.addEventListener("abort", () => {
38934
39201
  clearTimeout(timeoutId);
38935
39202
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -39719,7 +39986,7 @@ var Server = class extends Protocol {
39719
39986
  };
39720
39987
 
39721
39988
  // src/mcp/server.ts
39722
- var path46 = __toESM(require("path"));
39989
+ var path47 = __toESM(require("path"));
39723
39990
 
39724
39991
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
39725
39992
  var import_node_process2 = __toESM(require("node:process"), 1);
@@ -39802,12 +40069,12 @@ var StdioServerTransport = class {
39802
40069
  this.onclose?.();
39803
40070
  }
39804
40071
  send(message) {
39805
- return new Promise((resolve33) => {
40072
+ return new Promise((resolve34) => {
39806
40073
  const json = serializeMessage(message);
39807
40074
  if (this._stdout.write(json)) {
39808
- resolve33();
40075
+ resolve34();
39809
40076
  } else {
39810
- this._stdout.once("drain", resolve33);
40077
+ this._stdout.once("drain", resolve34);
39811
40078
  }
39812
40079
  });
39813
40080
  }
@@ -39890,10 +40157,7 @@ var LaunchEnvironmentInputSchema = external_exports.object({
39890
40157
  force: external_exports.boolean().default(false).describe(
39891
40158
  "Discard uncommitted changes in a dirty occupied worktree. Requires confirmReplace=true and explicit user approval \u2014 never set autonomously."
39892
40159
  ),
39893
- resume: external_exports.boolean().default(false).describe("Resume a failed or partial launch without creating a new worktree."),
39894
- purpose: external_exports.string().optional().describe(
39895
- "Optional session label stored in the slot registry (shown in status and occupied-slot warnings)."
39896
- )
40160
+ resume: external_exports.boolean().default(false).describe("Resume a failed or partial launch without creating a new worktree.")
39897
40161
  });
39898
40162
  var LaunchEnvironmentOutputSchema = ShellRunOutputSchema.extend({
39899
40163
  previewUrls: external_exports.record(external_exports.string()).optional(),
@@ -39907,8 +40171,7 @@ var LaunchEnvironmentOutputSchema = ShellRunOutputSchema.extend({
39907
40171
  worktreePath: external_exports.string().optional(),
39908
40172
  branch: external_exports.string().optional(),
39909
40173
  dirty: external_exports.boolean().optional(),
39910
- sessionCreatedAt: external_exports.string().optional(),
39911
- purpose: external_exports.string().optional()
40174
+ sessionCreatedAt: external_exports.string().optional()
39912
40175
  }).optional()
39913
40176
  });
39914
40177
  var PreflightEnvironmentInputSchema = external_exports.object({
@@ -40033,10 +40296,6 @@ var HAR_MCP_TOOLS = [
40033
40296
  resume: {
40034
40297
  type: "boolean",
40035
40298
  description: "Resume a failed or partial launch (status failed/starting) without --replace. Preserves worktree and env."
40036
- },
40037
- purpose: {
40038
- type: "string",
40039
- description: "Optional session label stored in the slot registry (shown in status and occupied-slot warnings)."
40040
40299
  }
40041
40300
  },
40042
40301
  ["agentId"]
@@ -40220,7 +40479,6 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
40220
40479
  confirmReplace: input.confirmReplace,
40221
40480
  force: input.force,
40222
40481
  resume: input.resume,
40223
- purpose: input.purpose,
40224
40482
  capture: true
40225
40483
  });
40226
40484
  const parsed = LaunchEnvironmentOutputSchema.parse(result);
@@ -40360,7 +40618,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
40360
40618
  const input = ControlUpInputSchema.parse({ ...args, repo });
40361
40619
  const result = await startControlAndSync({
40362
40620
  detach: input.detach,
40363
- cwd: path46.resolve(input.repo)
40621
+ cwd: path47.resolve(input.repo)
40364
40622
  });
40365
40623
  if (result.code !== 0) {
40366
40624
  return {
@@ -40423,8 +40681,10 @@ var mcpCommand = {
40423
40681
  };
40424
40682
 
40425
40683
  // src/cli/commands/telemetry.ts
40426
- var fs40 = __toESM(require("fs"));
40427
- var path47 = __toESM(require("path"));
40684
+ var path48 = __toESM(require("path"));
40685
+ init_control_config();
40686
+ init_otel_hooks();
40687
+ init_telemetry_config();
40428
40688
  function printSignals() {
40429
40689
  info("Collected when enabled (local Mission Control only):");
40430
40690
  for (const signal of TELEMETRY_SIGNALS) {
@@ -40448,6 +40708,8 @@ async function handleStatus3(argv) {
40448
40708
  controlApiUrl: apiUrl,
40449
40709
  controlReachable: reachable,
40450
40710
  otelEndpoint: `${apiUrl.replace(/\/$/, "")}/api/otel`,
40711
+ otelHooksHome: getOtelHooksHome(),
40712
+ otelHooksConfig: getOtelHooksConfigPath(),
40451
40713
  signalDescriptions: [...TELEMETRY_SIGNALS]
40452
40714
  };
40453
40715
  if (argv.json) {
@@ -40463,25 +40725,29 @@ async function handleStatus3(argv) {
40463
40725
  );
40464
40726
  info(`Mission Control: ${apiUrl} (${reachable ? "reachable" : "not reachable"})`);
40465
40727
  info(`OTLP endpoint: ${payload.otelEndpoint}`);
40728
+ info(`Hooks home: ${payload.otelHooksHome}`);
40466
40729
  printSignals();
40467
40730
  }
40468
40731
  async function handleOn(argv) {
40469
40732
  header("har telemetry on");
40470
40733
  const preference = writeTelemetryPreference(true, {
40471
40734
  prompts: argv.prompts === true ? true : void 0,
40472
- traces: argv.traces === true ? true : void 0
40735
+ traces: true
40473
40736
  });
40474
- success("Telemetry enabled (opt-out). Agent usage will be stored in local Mission Control.");
40737
+ success("Telemetry enabled. Cursor / Claude / Codex export via opentelemetry-hooks \u2192 Mission Control.");
40475
40738
  info(
40476
40739
  `Signals: metrics=${preference.signals.metrics} logs=${preference.signals.logs} prompts=${preference.signals.prompts} traces=${preference.signals.traces}`
40477
40740
  );
40478
40741
  if (preference.signals.prompts) {
40479
- warn("Prompt/response text will leave the agent machine for local Mission Control storage.");
40742
+ warn("Prompt text will leave the agent machine for local Mission Control (also used as session purpose).");
40480
40743
  }
40481
40744
  printSignals();
40482
40745
  const result = await ensureTelemetryInfrastructure({ startIfNeeded: true });
40483
40746
  if (result.message) success(result.message);
40484
40747
  if (result.warning) warn(result.warning);
40748
+ const hooks = ensureOtelHooks({ setupAgents: true });
40749
+ if (hooks.message) success(hooks.message);
40750
+ if (hooks.warning) warn(hooks.warning);
40485
40751
  if (result.otelReady) {
40486
40752
  info(`OTLP ingest: ${result.apiUrl.replace(/\/$/, "")}/api/otel`);
40487
40753
  info("Usage appears under Mission Control \u2192 Worktrees / Usage. Disable: har telemetry off");
@@ -40490,44 +40756,48 @@ async function handleOn(argv) {
40490
40756
  async function handleOff() {
40491
40757
  header("har telemetry off");
40492
40758
  writeTelemetryPreference(false);
40759
+ try {
40760
+ disableOtelHooksExport();
40761
+ } catch (err) {
40762
+ warn(`Could not refresh hooks config: ${err instanceof Error ? err.message : String(err)}`);
40763
+ }
40493
40764
  success("Telemetry disabled.");
40494
- info("Future launches will not inject OTEL exporters or auto-start Mission Control.");
40495
- info("Existing usage rows in Mission Control are kept. Historical data is not deleted.");
40765
+ info("Hooks stay registered but OTLP export is cleared in ~/.har/otel-hooks/otel_config.json.");
40766
+ info("Mission Control will not auto-start. Existing usage rows are kept.");
40496
40767
  }
40497
40768
  async function handleWriteEnv(argv) {
40498
- const repoPath = path47.resolve(argv.repo);
40769
+ const repoPath = path48.resolve(argv.repo);
40499
40770
  const harnessRoot = resolveHarnessRoot(repoPath);
40500
40771
  const session = readSlotRegistry(harnessRoot, argv.agentId);
40501
40772
  const workDir = argv.workDir ?? session?.workDir ?? repoPath;
40502
40773
  const branch = argv.branch ?? session?.branch;
40503
40774
  const suffix = argv.suffix ?? session?.suffix;
40504
- const purpose = argv.purpose ?? session?.purpose;
40505
40775
  const sessionKey = argv.sessionKey ?? buildSessionKey({
40506
40776
  branch,
40507
40777
  agentId: argv.agentId,
40508
40778
  suffix,
40509
40779
  createdAt: session?.createdAt
40510
40780
  });
40511
- const envFile = argv.envFile ?? path47.join(workDir, `.env.agent.${argv.agentId}`);
40512
- let otelReady = argv.otelReady;
40513
- if (otelReady === void 0 && isTelemetryEnabled()) {
40514
- const ensured = await ensureTelemetryInfrastructure({ startIfNeeded: false });
40515
- otelReady = ensured.otelReady;
40516
- }
40781
+ const envFile = argv.envFile ?? path48.join(workDir, `.env.agent.${argv.agentId}`);
40517
40782
  const attrs = {
40518
40783
  sessionKey,
40519
40784
  agentId: argv.agentId,
40520
40785
  repoPath,
40521
40786
  workDir,
40522
40787
  branch,
40523
- suffix,
40524
- purpose
40788
+ suffix
40525
40789
  };
40526
- appendTelemetryEnvToFile(envFile, attrs, { otelReady: otelReady !== false && isTelemetryEnabled() });
40527
- success(`Wrote telemetry env to ${envFile}`);
40790
+ appendTelemetryEnvToFile(envFile, attrs);
40791
+ if (isTelemetryEnabled()) {
40792
+ ensureOtelHooks({
40793
+ setupAgents: false,
40794
+ resourceAttributes: buildOtelResourceAttributes(attrs)
40795
+ });
40796
+ }
40797
+ success(`Wrote session attribution to ${envFile}`);
40528
40798
  }
40529
40799
  function handlePrintEnv(argv) {
40530
- const repoPath = path47.resolve(argv.repo);
40800
+ const repoPath = path48.resolve(argv.repo);
40531
40801
  const workDir = argv.workDir ?? repoPath;
40532
40802
  const sessionKey = buildSessionKey({
40533
40803
  branch: argv.branch,
@@ -40535,55 +40805,38 @@ function handlePrintEnv(argv) {
40535
40805
  suffix: argv.suffix
40536
40806
  });
40537
40807
  process.stdout.write(
40538
- buildTelemetryEnvBlock(
40539
- {
40540
- sessionKey,
40541
- agentId: argv.agentId,
40542
- repoPath,
40543
- workDir,
40544
- branch: argv.branch,
40545
- suffix: argv.suffix
40546
- },
40547
- { otelReady: argv.otelReady && isTelemetryEnabled() }
40548
- )
40808
+ buildTelemetryEnvBlock({
40809
+ sessionKey,
40810
+ agentId: argv.agentId,
40811
+ repoPath,
40812
+ workDir,
40813
+ branch: argv.branch,
40814
+ suffix: argv.suffix
40815
+ })
40549
40816
  );
40550
40817
  }
40551
- function handleCodexSnippet(argv) {
40552
- const repoPath = path47.resolve(argv.repo);
40553
- const harnessRoot = resolveHarnessRoot(repoPath);
40554
- const session = readSlotRegistry(harnessRoot, argv.agentId);
40555
- const workDir = argv.workDir ?? session?.workDir ?? repoPath;
40556
- const branch = argv.branch ?? session?.branch;
40557
- const suffix = argv.suffix ?? session?.suffix;
40558
- const sessionKey = buildSessionKey({
40559
- branch,
40560
- agentId: argv.agentId,
40561
- suffix,
40562
- createdAt: session?.createdAt
40563
- });
40564
- const snippet = buildCodexOtelSnippet({
40565
- sessionKey,
40566
- agentId: argv.agentId,
40567
- repoPath,
40568
- workDir,
40569
- branch,
40570
- suffix,
40571
- purpose: session?.purpose
40572
- });
40573
- if (argv.write) {
40574
- const outDir = path47.join(harnessRoot, ".har", "telemetry");
40575
- fs40.mkdirSync(outDir, { recursive: true });
40576
- const outPath = path47.join(outDir, "codex.otel.toml.snippet");
40577
- fs40.writeFileSync(outPath, snippet);
40578
- success(`Wrote ${outPath}`);
40579
- info("Merge the [otel] table into ~/.codex/config.toml manually.");
40580
- return;
40818
+ async function handleInstallHooks() {
40819
+ header("har telemetry install-hooks");
40820
+ if (!isTelemetryEnabled()) {
40821
+ warn("Telemetry is off \u2014 enabling preference so hooks can export.");
40822
+ writeTelemetryPreference(true, { traces: true });
40823
+ }
40824
+ const result = await ensureTelemetryInfrastructure({ startIfNeeded: true });
40825
+ if (result.message) success(result.message);
40826
+ if (result.warning) warn(result.warning);
40827
+ const hooks = ensureOtelHooks({ setupAgents: true });
40828
+ if (hooks.ok) {
40829
+ success(hooks.message ?? "opentelemetry-hooks installed and agents registered");
40830
+ info(`Config: ${hooks.configPath}`);
40831
+ info(`Wrapper: ${hooks.wrapperPath}`);
40832
+ } else {
40833
+ error(hooks.warning ?? "Failed to install opentelemetry-hooks");
40834
+ process.exitCode = 1;
40581
40835
  }
40582
- process.stdout.write(snippet);
40583
40836
  }
40584
40837
  var telemetryCommand = {
40585
40838
  command: "telemetry <subcommand>",
40586
- describe: "Agent usage telemetry (Claude Code / Codex \u2192 Mission Control)",
40839
+ describe: "Agent usage telemetry (Cursor / Claude / Codex via opentelemetry-hooks \u2192 Mission Control)",
40587
40840
  builder: (yargs) => yargs.command(
40588
40841
  "status",
40589
40842
  "Show telemetry preference and Mission Control reachability",
@@ -40593,34 +40846,40 @@ var telemetryCommand = {
40593
40846
  }
40594
40847
  ).command(
40595
40848
  "on",
40596
- "Enable telemetry (default) and ensure Mission Control is running",
40849
+ "Enable telemetry, ensure Mission Control, install/configure opentelemetry-hooks",
40597
40850
  (y2) => y2.option("prompts", {
40598
40851
  type: "boolean",
40599
40852
  default: false,
40600
- describe: "Opt in to shipping user/assistant prompt text to Mission Control"
40601
- }).option("traces", {
40602
- type: "boolean",
40603
- default: false,
40604
- describe: "Opt in to thin OTEL traces ingest (Claude beta)"
40853
+ describe: "Opt in to shipping user prompt text (also fills Mission Control purpose)"
40605
40854
  }),
40606
40855
  (argv) => {
40607
40856
  void handleOn({
40608
- prompts: argv.prompts,
40609
- traces: argv.traces
40857
+ prompts: argv.prompts
40610
40858
  });
40611
40859
  }
40612
40860
  ).command(
40613
40861
  "off",
40614
- "Disable telemetry (no OTEL injection, no MC auto-start)",
40862
+ "Disable telemetry (clear hooks OTLP endpoint, no MC auto-start)",
40615
40863
  () => {
40616
40864
  },
40617
40865
  () => {
40618
40866
  void handleOff();
40619
40867
  }
40868
+ ).command(
40869
+ "install-hooks",
40870
+ "Install opentelemetry-hooks and register Cursor / Claude / Codex",
40871
+ () => {
40872
+ },
40873
+ () => {
40874
+ void handleInstallHooks().catch((err) => {
40875
+ error(err instanceof Error ? err.message : String(err));
40876
+ process.exitCode = 1;
40877
+ });
40878
+ }
40620
40879
  ).command(
40621
40880
  "write-env",
40622
- "Append session + OTEL env vars to .env.agent.<id>",
40623
- (y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("env-file", { type: "string" }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("purpose", { type: "string" }).option("session-key", { type: "string" }).option("otel-ready", { type: "boolean" }),
40881
+ "Append HAR session attribution to .env.agent.<id> and refresh hooks config",
40882
+ (y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("env-file", { type: "string" }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("session-key", { type: "string" }),
40624
40883
  (argv) => handleWriteEnv({
40625
40884
  agentId: argv["agent-id"],
40626
40885
  repo: argv.repo,
@@ -40628,42 +40887,23 @@ var telemetryCommand = {
40628
40887
  workDir: argv["work-dir"],
40629
40888
  branch: argv.branch,
40630
40889
  suffix: argv.suffix,
40631
- purpose: argv.purpose,
40632
- sessionKey: argv["session-key"],
40633
- otelReady: argv["otel-ready"]
40890
+ sessionKey: argv["session-key"]
40634
40891
  }).catch((err) => {
40635
40892
  error(err instanceof Error ? err.message : String(err));
40636
40893
  process.exitCode = 1;
40637
40894
  })
40638
40895
  ).command(
40639
40896
  "print-env",
40640
- "Print telemetry env block to stdout",
40641
- (y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("otel-ready", { type: "boolean", default: true }),
40897
+ "Print session attribution env block to stdout",
40898
+ (y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }),
40642
40899
  (argv) => handlePrintEnv({
40643
40900
  agentId: argv["agent-id"],
40644
40901
  repo: argv.repo,
40645
40902
  workDir: argv["work-dir"],
40646
40903
  branch: argv.branch,
40647
- suffix: argv.suffix,
40648
- otelReady: argv["otel-ready"]
40649
- })
40650
- ).command(
40651
- "codex-snippet",
40652
- "Print or write a Codex [otel] config snippet",
40653
- (y2) => y2.option("agent-id", { type: "number", demandOption: true }).option("repo", { type: "string", default: "." }).option("work-dir", { type: "string" }).option("branch", { type: "string" }).option("suffix", { type: "string" }).option("write", {
40654
- type: "boolean",
40655
- default: false,
40656
- describe: "Write to .har/telemetry/codex.otel.toml.snippet"
40657
- }),
40658
- (argv) => handleCodexSnippet({
40659
- agentId: argv["agent-id"],
40660
- repo: argv.repo,
40661
- workDir: argv["work-dir"],
40662
- branch: argv.branch,
40663
- suffix: argv.suffix,
40664
- write: argv.write
40904
+ suffix: argv.suffix
40665
40905
  })
40666
- ).demandCommand(1, "Please specify a subcommand: status, on, off, write-env, print-env, codex-snippet"),
40906
+ ).demandCommand(1, "Please specify a subcommand: status, on, off, install-hooks, write-env, print-env"),
40667
40907
  handler: () => {
40668
40908
  }
40669
40909
  };