@harness-engineering/core 0.21.2 → 0.21.3

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.mjs CHANGED
@@ -84,15 +84,15 @@ function validateConfig(data, schema) {
84
84
  let message = "Configuration validation failed";
85
85
  const suggestions = [];
86
86
  if (firstError) {
87
- const path31 = firstError.path.join(".");
88
- const pathDisplay = path31 ? ` at "${path31}"` : "";
87
+ const path34 = firstError.path.join(".");
88
+ const pathDisplay = path34 ? ` at "${path34}"` : "";
89
89
  if (firstError.code === "invalid_type") {
90
90
  const received = firstError.received;
91
91
  const expected = firstError.expected;
92
92
  if (received === "undefined") {
93
93
  code = "MISSING_FIELD";
94
94
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
95
- suggestions.push(`Field "${path31}" is required and must be of type "${expected}"`);
95
+ suggestions.push(`Field "${path34}" is required and must be of type "${expected}"`);
96
96
  } else {
97
97
  code = "INVALID_TYPE";
98
98
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -311,27 +311,27 @@ function extractSections(content) {
311
311
  }
312
312
  return sections.map((section) => buildAgentMapSection(section, lines));
313
313
  }
314
- function isExternalLink(path31) {
315
- return path31.startsWith("http://") || path31.startsWith("https://") || path31.startsWith("#") || path31.startsWith("mailto:");
314
+ function isExternalLink(path34) {
315
+ return path34.startsWith("http://") || path34.startsWith("https://") || path34.startsWith("#") || path34.startsWith("mailto:");
316
316
  }
317
317
  function resolveLinkPath(linkPath, baseDir) {
318
318
  return linkPath.startsWith(".") ? join(baseDir, linkPath) : linkPath;
319
319
  }
320
- async function validateAgentsMap(path31 = "./AGENTS.md") {
321
- const contentResult = await readFileContent(path31);
320
+ async function validateAgentsMap(path34 = "./AGENTS.md") {
321
+ const contentResult = await readFileContent(path34);
322
322
  if (!contentResult.ok) {
323
323
  return Err(
324
324
  createError(
325
325
  "PARSE_ERROR",
326
326
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
327
- { path: path31 },
327
+ { path: path34 },
328
328
  ["Ensure the file exists", "Check file permissions"]
329
329
  )
330
330
  );
331
331
  }
332
332
  const content = contentResult.value;
333
333
  const sections = extractSections(content);
334
- const baseDir = dirname(path31);
334
+ const baseDir = dirname(path34);
335
335
  const sectionTitles = sections.map((s) => s.title);
336
336
  const missingSections = REQUIRED_SECTIONS.filter(
337
337
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -472,8 +472,8 @@ async function checkDocCoverage(domain, options = {}) {
472
472
 
473
473
  // src/context/knowledge-map.ts
474
474
  import { join as join2, basename as basename2 } from "path";
475
- function suggestFix(path31, existingFiles) {
476
- const targetName = basename2(path31).toLowerCase();
475
+ function suggestFix(path34, existingFiles) {
476
+ const targetName = basename2(path34).toLowerCase();
477
477
  const similar = existingFiles.find((file) => {
478
478
  const fileName = basename2(file).toLowerCase();
479
479
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -481,7 +481,7 @@ function suggestFix(path31, existingFiles) {
481
481
  if (similar) {
482
482
  return `Did you mean "${similar}"?`;
483
483
  }
484
- return `Create the file "${path31}" or remove the link`;
484
+ return `Create the file "${path34}" or remove the link`;
485
485
  }
486
486
  async function validateKnowledgeMap(rootDir = process.cwd()) {
487
487
  const agentsPath = join2(rootDir, "AGENTS.md");
@@ -831,8 +831,8 @@ function createBoundaryValidator(schema, name) {
831
831
  return Ok(result.data);
832
832
  }
833
833
  const suggestions = result.error.issues.map((issue) => {
834
- const path31 = issue.path.join(".");
835
- return path31 ? `${path31}: ${issue.message}` : issue.message;
834
+ const path34 = issue.path.join(".");
835
+ return path34 ? `${path34}: ${issue.message}` : issue.message;
836
836
  });
837
837
  return Err(
838
838
  createError(
@@ -1466,11 +1466,11 @@ function processExportListSpecifiers(exportDecl, exports) {
1466
1466
  var TypeScriptParser = class {
1467
1467
  name = "typescript";
1468
1468
  extensions = [".ts", ".tsx", ".mts", ".cts"];
1469
- async parseFile(path31) {
1470
- const contentResult = await readFileContent(path31);
1469
+ async parseFile(path34) {
1470
+ const contentResult = await readFileContent(path34);
1471
1471
  if (!contentResult.ok) {
1472
1472
  return Err(
1473
- createParseError("NOT_FOUND", `File not found: ${path31}`, { path: path31 }, [
1473
+ createParseError("NOT_FOUND", `File not found: ${path34}`, { path: path34 }, [
1474
1474
  "Check that the file exists",
1475
1475
  "Verify the path is correct"
1476
1476
  ])
@@ -1480,7 +1480,7 @@ var TypeScriptParser = class {
1480
1480
  const ast = parse(contentResult.value, {
1481
1481
  loc: true,
1482
1482
  range: true,
1483
- jsx: path31.endsWith(".tsx"),
1483
+ jsx: path34.endsWith(".tsx"),
1484
1484
  errorOnUnknownASTType: false
1485
1485
  });
1486
1486
  return Ok({
@@ -1491,7 +1491,7 @@ var TypeScriptParser = class {
1491
1491
  } catch (e) {
1492
1492
  const error = e;
1493
1493
  return Err(
1494
- createParseError("SYNTAX_ERROR", `Failed to parse ${path31}: ${error.message}`, { path: path31 }, [
1494
+ createParseError("SYNTAX_ERROR", `Failed to parse ${path34}: ${error.message}`, { path: path34 }, [
1495
1495
  "Check for syntax errors in the file",
1496
1496
  "Ensure valid TypeScript syntax"
1497
1497
  ])
@@ -1676,22 +1676,22 @@ function extractInlineRefs(content) {
1676
1676
  }
1677
1677
  return refs;
1678
1678
  }
1679
- async function parseDocumentationFile(path31) {
1680
- const contentResult = await readFileContent(path31);
1679
+ async function parseDocumentationFile(path34) {
1680
+ const contentResult = await readFileContent(path34);
1681
1681
  if (!contentResult.ok) {
1682
1682
  return Err(
1683
1683
  createEntropyError(
1684
1684
  "PARSE_ERROR",
1685
- `Failed to read documentation file: ${path31}`,
1686
- { file: path31 },
1685
+ `Failed to read documentation file: ${path34}`,
1686
+ { file: path34 },
1687
1687
  ["Check that the file exists"]
1688
1688
  )
1689
1689
  );
1690
1690
  }
1691
1691
  const content = contentResult.value;
1692
- const type = path31.endsWith(".md") ? "markdown" : "text";
1692
+ const type = path34.endsWith(".md") ? "markdown" : "text";
1693
1693
  return Ok({
1694
- path: path31,
1694
+ path: path34,
1695
1695
  type,
1696
1696
  content,
1697
1697
  codeBlocks: extractCodeBlocks(content),
@@ -4002,12 +4002,12 @@ function parseDiffHeader(part) {
4002
4002
  return headerMatch[2];
4003
4003
  }
4004
4004
  function parseDiffPart(part) {
4005
- const path31 = parseDiffHeader(part);
4006
- if (!path31) return null;
4005
+ const path34 = parseDiffHeader(part);
4006
+ if (!path34) return null;
4007
4007
  const additionRegex = /^\+(?!\+\+)/gm;
4008
4008
  const deletionRegex = /^-(?!--)/gm;
4009
4009
  return {
4010
- path: path31,
4010
+ path: path34,
4011
4011
  status: detectFileStatus(part),
4012
4012
  additions: (part.match(additionRegex) || []).length,
4013
4013
  deletions: (part.match(deletionRegex) || []).length
@@ -6349,6 +6349,35 @@ async function saveState(projectPath, state, stream, session) {
6349
6349
  import * as fs11 from "fs";
6350
6350
  import * as path8 from "path";
6351
6351
  import * as crypto2 from "crypto";
6352
+
6353
+ // src/compaction/envelope.ts
6354
+ function estimateTokens(content) {
6355
+ return Math.ceil(content.length / 4);
6356
+ }
6357
+ function serializeEnvelope(envelope) {
6358
+ const { meta, sections } = envelope;
6359
+ const strategyLabel = meta.strategy.length > 0 ? meta.strategy.join("+") : "none";
6360
+ let cacheLabel = "";
6361
+ if (meta.cached) {
6362
+ if (meta.cacheReadTokens != null && meta.cacheInputTokens != null && meta.cacheInputTokens > 0) {
6363
+ const hitPct = Math.round(meta.cacheReadTokens / meta.cacheInputTokens * 100);
6364
+ const readFormatted = meta.cacheReadTokens >= 1e3 ? (meta.cacheReadTokens / 1e3).toFixed(1) + "K" : String(meta.cacheReadTokens);
6365
+ cacheLabel = ` [cached | cache: ${readFormatted} read, ${hitPct}% hit]`;
6366
+ } else {
6367
+ cacheLabel = " [cached]";
6368
+ }
6369
+ }
6370
+ const header = `<!-- packed: ${strategyLabel} | ${meta.originalTokenEstimate}\u2192${meta.compactedTokenEstimate} tokens (-${meta.reductionPct}%)${cacheLabel} -->`;
6371
+ if (sections.length === 0) {
6372
+ return header;
6373
+ }
6374
+ const body = sections.map((section) => `### [${section.source}]
6375
+ ${section.content}`).join("\n\n");
6376
+ return `${header}
6377
+ ${body}`;
6378
+ }
6379
+
6380
+ // src/state/learnings-content.ts
6352
6381
  function parseFrontmatter2(line) {
6353
6382
  const match = line.match(/^<!--\s+hash:([a-f0-9]+)(?:\s+tags:([^\s]+))?\s+-->/);
6354
6383
  if (!match) return null;
@@ -6452,9 +6481,6 @@ function analyzeLearningPatterns(entries) {
6452
6481
  }
6453
6482
  return patterns.sort((a, b) => b.count - a.count);
6454
6483
  }
6455
- function estimateTokens(text) {
6456
- return Math.ceil(text.length / 4);
6457
- }
6458
6484
  function scoreRelevance(entry, intent) {
6459
6485
  if (!intent || intent.trim() === "") return 0;
6460
6486
  const intentWords = intent.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
@@ -12414,9 +12440,9 @@ async function resolveWasmPath(grammarName) {
12414
12440
  const { createRequire } = await import("module");
12415
12441
  const require2 = createRequire(import.meta.url ?? __filename);
12416
12442
  const pkgPath = require2.resolve("tree-sitter-wasms/package.json");
12417
- const path31 = await import("path");
12418
- const pkgDir = path31.dirname(pkgPath);
12419
- return path31.join(pkgDir, "out", `${grammarName}.wasm`);
12443
+ const path34 = await import("path");
12444
+ const pkgDir = path34.dirname(pkgPath);
12445
+ return path34.join(pkgDir, "out", `${grammarName}.wasm`);
12420
12446
  }
12421
12447
  async function loadLanguage(lang) {
12422
12448
  const grammarName = GRAMMAR_MAP[lang];
@@ -13296,13 +13322,457 @@ function parseCCRecords() {
13296
13322
  return records;
13297
13323
  }
13298
13324
 
13299
- // src/index.ts
13300
- var VERSION = "0.21.1";
13325
+ // src/adoption/reader.ts
13326
+ import * as fs32 from "fs";
13327
+ import * as path31 from "path";
13328
+ function parseLine2(line, lineNumber) {
13329
+ try {
13330
+ const parsed = JSON.parse(line);
13331
+ if (typeof parsed.skill !== "string" || typeof parsed.startedAt !== "string" || typeof parsed.duration !== "number" || typeof parsed.outcome !== "string" || !Array.isArray(parsed.phasesReached)) {
13332
+ process.stderr.write(
13333
+ `[harness adoption] Skipping malformed JSONL line ${lineNumber}: missing required fields
13334
+ `
13335
+ );
13336
+ return null;
13337
+ }
13338
+ return parsed;
13339
+ } catch {
13340
+ process.stderr.write(`[harness adoption] Skipping malformed JSONL line ${lineNumber}
13341
+ `);
13342
+ return null;
13343
+ }
13344
+ }
13345
+ function readAdoptionRecords(projectRoot) {
13346
+ const adoptionFile = path31.join(projectRoot, ".harness", "metrics", "adoption.jsonl");
13347
+ let raw;
13348
+ try {
13349
+ raw = fs32.readFileSync(adoptionFile, "utf-8");
13350
+ } catch {
13351
+ return [];
13352
+ }
13353
+ const records = [];
13354
+ const lines = raw.split("\n");
13355
+ for (let i = 0; i < lines.length; i++) {
13356
+ const line = lines[i]?.trim();
13357
+ if (!line) continue;
13358
+ const record = parseLine2(line, i + 1);
13359
+ if (record) {
13360
+ records.push(record);
13361
+ }
13362
+ }
13363
+ return records;
13364
+ }
13365
+
13366
+ // src/adoption/aggregator.ts
13367
+ function aggregateBySkill(records) {
13368
+ if (records.length === 0) return [];
13369
+ const skillMap = /* @__PURE__ */ new Map();
13370
+ for (const record of records) {
13371
+ if (!skillMap.has(record.skill)) {
13372
+ const entry = { records: [] };
13373
+ if (record.tier != null) entry.tier = record.tier;
13374
+ skillMap.set(record.skill, entry);
13375
+ }
13376
+ skillMap.get(record.skill).records.push(record);
13377
+ }
13378
+ const results = [];
13379
+ for (const [skill, bucket] of skillMap) {
13380
+ const invocations = bucket.records.length;
13381
+ const completedCount = bucket.records.filter((r) => r.outcome === "completed").length;
13382
+ const totalDuration = bucket.records.reduce((sum, r) => sum + r.duration, 0);
13383
+ const timestamps = bucket.records.map((r) => r.startedAt).sort();
13384
+ const summary = {
13385
+ skill,
13386
+ invocations,
13387
+ successRate: completedCount / invocations,
13388
+ avgDuration: totalDuration / invocations,
13389
+ lastUsed: timestamps[timestamps.length - 1]
13390
+ };
13391
+ if (bucket.tier != null) summary.tier = bucket.tier;
13392
+ results.push(summary);
13393
+ }
13394
+ results.sort((a, b) => b.invocations - a.invocations);
13395
+ return results;
13396
+ }
13397
+ function aggregateByDay2(records) {
13398
+ if (records.length === 0) return [];
13399
+ const dayMap = /* @__PURE__ */ new Map();
13400
+ for (const record of records) {
13401
+ const date = record.startedAt.slice(0, 10);
13402
+ if (!dayMap.has(date)) {
13403
+ dayMap.set(date, { invocations: 0, skills: /* @__PURE__ */ new Set() });
13404
+ }
13405
+ const bucket = dayMap.get(date);
13406
+ bucket.invocations++;
13407
+ bucket.skills.add(record.skill);
13408
+ }
13409
+ const results = [];
13410
+ for (const [date, bucket] of dayMap) {
13411
+ results.push({
13412
+ date,
13413
+ invocations: bucket.invocations,
13414
+ uniqueSkills: bucket.skills.size
13415
+ });
13416
+ }
13417
+ results.sort((a, b) => b.date.localeCompare(a.date));
13418
+ return results;
13419
+ }
13420
+ function topSkills(records, n) {
13421
+ return aggregateBySkill(records).slice(0, n);
13422
+ }
13423
+
13424
+ // src/compaction/strategies/structural.ts
13425
+ function isEmptyObject(v) {
13426
+ return typeof v === "object" && v !== null && !Array.isArray(v) && Object.keys(v).length === 0;
13427
+ }
13428
+ function isRetainable(v) {
13429
+ return v !== void 0 && v !== "" && v !== null && !isEmptyObject(v);
13430
+ }
13431
+ function cleanArray(value) {
13432
+ const cleaned = value.map(cleanValue).filter(isRetainable);
13433
+ if (cleaned.length === 0) return void 0;
13434
+ if (cleaned.length === 1) return cleaned[0];
13435
+ return cleaned;
13436
+ }
13437
+ function cleanRecord(value) {
13438
+ const cleaned = {};
13439
+ for (const [k, v] of Object.entries(value)) {
13440
+ const result = cleanValue(v);
13441
+ if (isRetainable(result)) {
13442
+ cleaned[k] = result;
13443
+ }
13444
+ }
13445
+ if (Object.keys(cleaned).length === 0) return void 0;
13446
+ return cleaned;
13447
+ }
13448
+ function cleanValue(value) {
13449
+ if (value === null || value === void 0) return void 0;
13450
+ if (typeof value === "string") return value.replace(/\s+/g, " ").trim();
13451
+ if (Array.isArray(value)) return cleanArray(value);
13452
+ if (typeof value === "object") return cleanRecord(value);
13453
+ return value;
13454
+ }
13455
+ var StructuralStrategy = class {
13456
+ name = "structural";
13457
+ lossy = false;
13458
+ apply(content, _budget) {
13459
+ let parsed;
13460
+ try {
13461
+ parsed = JSON.parse(content);
13462
+ } catch {
13463
+ return content;
13464
+ }
13465
+ const cleaned = cleanValue(parsed);
13466
+ return JSON.stringify(cleaned) ?? "";
13467
+ }
13468
+ };
13469
+
13470
+ // src/compaction/strategies/truncation.ts
13471
+ var DEFAULT_TOKEN_BUDGET = 4e3;
13472
+ var CHARS_PER_TOKEN = 4;
13473
+ var TRUNCATION_MARKER = "\n[truncated \u2014 prioritized truncation applied]";
13474
+ function lineScore(line) {
13475
+ let score = 0;
13476
+ if (/\/[\w./-]/.test(line)) score += 40;
13477
+ if (/error|Error|ERROR|fail|FAIL|status/i.test(line)) score += 35;
13478
+ if (/\b[A-Z][a-z]+[A-Z]/.test(line) || /\b[a-z]+[A-Z]/.test(line)) score += 20;
13479
+ if (line.trim().length < 40) score += 10;
13480
+ return score;
13481
+ }
13482
+ function selectLines(lines, charBudget) {
13483
+ const scored = lines.map((line, idx) => ({ line, idx, score: lineScore(line) }));
13484
+ scored.sort((a, b) => b.score - a.score || a.idx - b.idx);
13485
+ const kept = [];
13486
+ let used = 0;
13487
+ for (const item of scored) {
13488
+ const lineLen = item.line.length + 1;
13489
+ if (used + lineLen > charBudget) continue;
13490
+ kept.push({ line: item.line, idx: item.idx });
13491
+ used += lineLen;
13492
+ }
13493
+ kept.sort((a, b) => a.idx - b.idx);
13494
+ return kept;
13495
+ }
13496
+ var TruncationStrategy = class {
13497
+ name = "truncate";
13498
+ lossy = false;
13499
+ // deliberate: spec Decision 2 — truncation is classified lossless at the pipeline level
13500
+ apply(content, budget = DEFAULT_TOKEN_BUDGET) {
13501
+ if (!content) return content;
13502
+ const charBudget = budget * CHARS_PER_TOKEN;
13503
+ if (content.length <= charBudget) return content;
13504
+ const lines = content.split("\n");
13505
+ const available = charBudget - TRUNCATION_MARKER.length;
13506
+ const kept = available > 0 ? selectLines(lines, available) : [{ line: (lines[0] ?? "").slice(0, charBudget), idx: 0 }];
13507
+ return kept.map((k) => k.line).join("\n") + TRUNCATION_MARKER;
13508
+ }
13509
+ };
13510
+
13511
+ // src/compaction/pipeline.ts
13512
+ var CompactionPipeline = class {
13513
+ strategies;
13514
+ constructor(strategies) {
13515
+ this.strategies = strategies;
13516
+ }
13517
+ /** The ordered list of strategy names in this pipeline. */
13518
+ get strategyNames() {
13519
+ return this.strategies.map((s) => s.name);
13520
+ }
13521
+ /**
13522
+ * Apply all strategies in order.
13523
+ * @param content — input string
13524
+ * @param budget — optional token budget forwarded to each strategy
13525
+ */
13526
+ apply(content, budget) {
13527
+ return this.strategies.reduce((current, strategy) => {
13528
+ return strategy.apply(current, budget);
13529
+ }, content);
13530
+ }
13531
+ };
13532
+
13533
+ // src/caching/stability.ts
13534
+ import { NODE_STABILITY } from "@harness-engineering/graph";
13535
+ var STABILITY_LOOKUP = {};
13536
+ for (const [key, tier] of Object.entries(NODE_STABILITY)) {
13537
+ STABILITY_LOOKUP[key] = tier;
13538
+ STABILITY_LOOKUP[key.toLowerCase()] = tier;
13539
+ }
13540
+ STABILITY_LOOKUP["packed_summary"] = "session";
13541
+ STABILITY_LOOKUP["skill"] = "static";
13542
+ function resolveStability(contentType) {
13543
+ return STABILITY_LOOKUP[contentType] ?? "ephemeral";
13544
+ }
13545
+
13546
+ // src/caching/adapters/anthropic.ts
13547
+ var TIER_ORDER = {
13548
+ static: 0,
13549
+ session: 1,
13550
+ ephemeral: 2
13551
+ };
13552
+ var AnthropicCacheAdapter = class {
13553
+ provider = "claude";
13554
+ wrapSystemBlock(content, stability) {
13555
+ if (stability === "ephemeral") {
13556
+ return { type: "text", text: content };
13557
+ }
13558
+ const ttl = stability === "static" ? "1h" : void 0;
13559
+ return {
13560
+ type: "text",
13561
+ text: content,
13562
+ cache_control: {
13563
+ type: "ephemeral",
13564
+ ...ttl !== void 0 && { ttl }
13565
+ }
13566
+ };
13567
+ }
13568
+ wrapTools(tools, stability) {
13569
+ if (tools.length === 0 || stability === "ephemeral") {
13570
+ return { tools: tools.map((t) => ({ ...t })) };
13571
+ }
13572
+ const wrapped = tools.map((t) => ({ ...t }));
13573
+ const last = wrapped[wrapped.length - 1];
13574
+ if (last) {
13575
+ last.cache_control = { type: "ephemeral" };
13576
+ }
13577
+ return { tools: wrapped };
13578
+ }
13579
+ orderContent(blocks) {
13580
+ return [...blocks].sort((a, b) => TIER_ORDER[a.stability] - TIER_ORDER[b.stability]);
13581
+ }
13582
+ parseCacheUsage(response) {
13583
+ const resp = response;
13584
+ const usage = resp?.usage;
13585
+ return {
13586
+ cacheCreationTokens: usage?.cache_creation_input_tokens ?? 0,
13587
+ cacheReadTokens: usage?.cache_read_input_tokens ?? 0
13588
+ };
13589
+ }
13590
+ };
13591
+
13592
+ // src/caching/adapters/openai.ts
13593
+ var TIER_ORDER2 = {
13594
+ static: 0,
13595
+ session: 1,
13596
+ ephemeral: 2
13597
+ };
13598
+ var OpenAICacheAdapter = class {
13599
+ provider = "openai";
13600
+ wrapSystemBlock(content, _stability) {
13601
+ return { type: "text", text: content };
13602
+ }
13603
+ wrapTools(tools, _stability) {
13604
+ return { tools: tools.map((t) => ({ ...t })) };
13605
+ }
13606
+ orderContent(blocks) {
13607
+ return [...blocks].sort((a, b) => TIER_ORDER2[a.stability] - TIER_ORDER2[b.stability]);
13608
+ }
13609
+ parseCacheUsage(response) {
13610
+ const resp = response;
13611
+ const usage = resp?.usage;
13612
+ const details = usage?.prompt_tokens_details;
13613
+ return {
13614
+ cacheCreationTokens: 0,
13615
+ cacheReadTokens: details?.cached_tokens ?? 0
13616
+ };
13617
+ }
13618
+ };
13619
+
13620
+ // src/caching/adapters/gemini.ts
13621
+ var TIER_ORDER3 = {
13622
+ static: 0,
13623
+ session: 1,
13624
+ ephemeral: 2
13625
+ };
13626
+ var CACHED_CONTENT_MARKER = "cachedContents:pending";
13627
+ var GeminiCacheAdapter = class {
13628
+ provider = "gemini";
13629
+ wrapSystemBlock(content, stability) {
13630
+ if (stability === "static") {
13631
+ return {
13632
+ type: "text",
13633
+ text: content,
13634
+ cachedContentRef: CACHED_CONTENT_MARKER
13635
+ };
13636
+ }
13637
+ return { type: "text", text: content };
13638
+ }
13639
+ wrapTools(tools, _stability) {
13640
+ return { tools: tools.map((t) => ({ ...t })) };
13641
+ }
13642
+ orderContent(blocks) {
13643
+ return [...blocks].sort((a, b) => TIER_ORDER3[a.stability] - TIER_ORDER3[b.stability]);
13644
+ }
13645
+ parseCacheUsage(response) {
13646
+ const resp = response;
13647
+ const metadata = resp?.usageMetadata;
13648
+ return {
13649
+ cacheCreationTokens: 0,
13650
+ cacheReadTokens: metadata?.cachedContentTokenCount ?? 0
13651
+ };
13652
+ }
13653
+ };
13654
+
13655
+ // src/telemetry/consent.ts
13656
+ import * as fs34 from "fs";
13657
+ import * as path33 from "path";
13658
+
13659
+ // src/telemetry/install-id.ts
13660
+ import * as fs33 from "fs";
13661
+ import * as path32 from "path";
13662
+ import * as crypto4 from "crypto";
13663
+ function getOrCreateInstallId(projectRoot) {
13664
+ const harnessDir = path32.join(projectRoot, ".harness");
13665
+ const installIdFile = path32.join(harnessDir, ".install-id");
13666
+ const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
13667
+ try {
13668
+ const existing = fs33.readFileSync(installIdFile, "utf-8").trim();
13669
+ if (UUID_V4_RE.test(existing)) {
13670
+ return existing;
13671
+ }
13672
+ } catch {
13673
+ }
13674
+ const id = crypto4.randomUUID();
13675
+ fs33.mkdirSync(harnessDir, { recursive: true });
13676
+ fs33.writeFileSync(installIdFile, id, { encoding: "utf-8", mode: 384 });
13677
+ return id;
13678
+ }
13679
+
13680
+ // src/telemetry/consent.ts
13681
+ function readIdentity(projectRoot) {
13682
+ const filePath = path33.join(projectRoot, ".harness", "telemetry.json");
13683
+ try {
13684
+ const raw = fs34.readFileSync(filePath, "utf-8");
13685
+ const parsed = JSON.parse(raw);
13686
+ if (parsed && typeof parsed === "object" && parsed.identity) {
13687
+ const { project, team, alias } = parsed.identity;
13688
+ const identity = {};
13689
+ if (typeof project === "string") identity.project = project;
13690
+ if (typeof team === "string") identity.team = team;
13691
+ if (typeof alias === "string") identity.alias = alias;
13692
+ return identity;
13693
+ }
13694
+ return {};
13695
+ } catch {
13696
+ return {};
13697
+ }
13698
+ }
13699
+ function resolveConsent(projectRoot, config) {
13700
+ if (process.env.DO_NOT_TRACK === "1") return { allowed: false };
13701
+ if (process.env.HARNESS_TELEMETRY_OPTOUT === "1") return { allowed: false };
13702
+ const enabled = config?.enabled ?? true;
13703
+ if (!enabled) return { allowed: false };
13704
+ const installId = getOrCreateInstallId(projectRoot);
13705
+ const identity = readIdentity(projectRoot);
13706
+ return { allowed: true, installId, identity };
13707
+ }
13708
+
13709
+ // src/version.ts
13710
+ var VERSION = "0.21.3";
13711
+
13712
+ // src/telemetry/collector.ts
13713
+ function mapOutcome(outcome) {
13714
+ return outcome === "completed" ? "success" : "failure";
13715
+ }
13716
+ function collectEvents(projectRoot, consent) {
13717
+ const records = readAdoptionRecords(projectRoot);
13718
+ if (records.length === 0) return [];
13719
+ const { installId, identity } = consent;
13720
+ const distinctId = identity.alias ?? installId;
13721
+ return records.map(
13722
+ (record) => ({
13723
+ event: "skill_invocation",
13724
+ distinctId,
13725
+ timestamp: record.startedAt,
13726
+ properties: {
13727
+ installId,
13728
+ os: process.platform,
13729
+ nodeVersion: process.version,
13730
+ harnessVersion: VERSION,
13731
+ skillName: record.skill,
13732
+ duration: record.duration,
13733
+ outcome: mapOutcome(record.outcome),
13734
+ phasesReached: record.phasesReached,
13735
+ ...identity.project ? { project: identity.project } : {},
13736
+ ...identity.team ? { team: identity.team } : {}
13737
+ }
13738
+ })
13739
+ );
13740
+ }
13741
+
13742
+ // src/telemetry/transport.ts
13743
+ var POSTHOG_BATCH_URL = "https://app.posthog.com/batch";
13744
+ var MAX_ATTEMPTS = 3;
13745
+ var TIMEOUT_MS = 5e3;
13746
+ function sleep2(ms) {
13747
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
13748
+ }
13749
+ async function send(events, apiKey) {
13750
+ if (events.length === 0) return;
13751
+ const payload = { api_key: apiKey, batch: events };
13752
+ const body = JSON.stringify(payload);
13753
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
13754
+ try {
13755
+ const res = await fetch(POSTHOG_BATCH_URL, {
13756
+ method: "POST",
13757
+ headers: { "Content-Type": "application/json" },
13758
+ body,
13759
+ signal: AbortSignal.timeout(TIMEOUT_MS)
13760
+ });
13761
+ if (res.ok) return;
13762
+ if (res.status < 500) return;
13763
+ } catch {
13764
+ }
13765
+ if (attempt < MAX_ATTEMPTS - 1) {
13766
+ await sleep2(1e3 * (attempt + 1));
13767
+ }
13768
+ }
13769
+ }
13301
13770
  export {
13302
13771
  AGENT_DESCRIPTORS,
13303
13772
  ARCHITECTURE_DESCRIPTOR,
13304
13773
  AdjustedForecastSchema,
13305
13774
  AgentActionEmitter,
13775
+ AnthropicCacheAdapter,
13306
13776
  ArchBaselineManager,
13307
13777
  ArchBaselineSchema,
13308
13778
  ArchConfigSchema,
@@ -13322,6 +13792,7 @@ export {
13322
13792
  CategorySnapshotSchema,
13323
13793
  ChecklistBuilder,
13324
13794
  CircularDepsCollector,
13795
+ CompactionPipeline,
13325
13796
  ComplexityCollector,
13326
13797
  ConfidenceTierSchema,
13327
13798
  ConfirmationSchema,
@@ -13336,6 +13807,7 @@ export {
13336
13807
  DEFAULT_STABILITY_THRESHOLDS,
13337
13808
  DEFAULT_STATE,
13338
13809
  DEFAULT_STREAM_INDEX,
13810
+ DEFAULT_TOKEN_BUDGET,
13339
13811
  DESTRUCTIVE_BASH,
13340
13812
  DepDepthCollector,
13341
13813
  DirectionSchema,
@@ -13349,6 +13821,7 @@ export {
13349
13821
  ForbiddenImportCollector,
13350
13822
  GateConfigSchema,
13351
13823
  GateResultSchema,
13824
+ GeminiCacheAdapter,
13352
13825
  GitHubIssuesSyncAdapter,
13353
13826
  HandoffSchema,
13354
13827
  HarnessStateSchema,
@@ -13363,6 +13836,7 @@ export {
13363
13836
  NoOpExecutor,
13364
13837
  NoOpSink,
13365
13838
  NoOpTelemetryAdapter,
13839
+ OpenAICacheAdapter,
13366
13840
  PatternConfigSchema,
13367
13841
  PredictionEngine,
13368
13842
  PredictionOptionsSchema,
@@ -13390,6 +13864,7 @@ export {
13390
13864
  StabilityForecastSchema,
13391
13865
  StreamIndexSchema,
13392
13866
  StreamInfoSchema,
13867
+ StructuralStrategy,
13393
13868
  ThresholdConfigSchema,
13394
13869
  TimelineFileSchema,
13395
13870
  TimelineManager,
@@ -13397,13 +13872,16 @@ export {
13397
13872
  TransitionSchema,
13398
13873
  TrendLineSchema,
13399
13874
  TrendResultSchema,
13875
+ TruncationStrategy,
13400
13876
  TypeScriptParser,
13401
13877
  VERSION,
13402
13878
  ViolationSchema,
13403
13879
  addProvenance,
13404
13880
  agentConfigRules,
13881
+ aggregateByDay2 as aggregateAdoptionByDay,
13405
13882
  aggregateByDay,
13406
13883
  aggregateBySession,
13884
+ aggregateBySkill,
13407
13885
  analyzeDiff,
13408
13886
  analyzeLearningPatterns,
13409
13887
  appendFailure,
@@ -13435,6 +13913,7 @@ export {
13435
13913
  clearFailuresCache,
13436
13914
  clearLearningsCache,
13437
13915
  clearTaint,
13916
+ collectEvents,
13438
13917
  computeContentHash,
13439
13918
  computeOverallSeverity,
13440
13919
  computeScanExitCode,
@@ -13474,6 +13953,7 @@ export {
13474
13953
  determineAssessment,
13475
13954
  diff,
13476
13955
  emitEvent,
13956
+ estimateTokens,
13477
13957
  executeWorkflow,
13478
13958
  expressRules,
13479
13959
  extractBundle,
@@ -13495,6 +13975,7 @@ export {
13495
13975
  getFeedbackConfig,
13496
13976
  getInjectionPatterns,
13497
13977
  getModelPrice,
13978
+ getOrCreateInstallId,
13498
13979
  getOutline,
13499
13980
  getParser,
13500
13981
  getPhaseCategories,
@@ -13547,8 +14028,10 @@ export {
13547
14028
  promoteSessionLearnings,
13548
14029
  pruneLearnings,
13549
14030
  reactRules,
14031
+ readAdoptionRecords,
13550
14032
  readCheckState,
13551
14033
  readCostRecords,
14034
+ readIdentity,
13552
14035
  readLockfile,
13553
14036
  readSessionSection,
13554
14037
  readSessionSections,
@@ -13559,11 +14042,13 @@ export {
13559
14042
  requestPeerReview,
13560
14043
  resetFeedbackConfig,
13561
14044
  resetParserCache,
14045
+ resolveConsent,
13562
14046
  resolveFileToLayer,
13563
14047
  resolveModelTier,
13564
14048
  resolveReverseStatus,
13565
14049
  resolveRuleSeverity,
13566
14050
  resolveSessionDir,
14051
+ resolveStability,
13567
14052
  resolveStreamPath,
13568
14053
  resolveThresholds,
13569
14054
  runAll,
@@ -13585,6 +14070,8 @@ export {
13585
14070
  scoreRoadmapCandidates,
13586
14071
  searchSymbols,
13587
14072
  secretRules,
14073
+ send,
14074
+ serializeEnvelope,
13588
14075
  serializeRoadmap,
13589
14076
  setActiveStream,
13590
14077
  sharpEdgesRules,
@@ -13595,6 +14082,7 @@ export {
13595
14082
  syncRoadmap,
13596
14083
  syncToExternal,
13597
14084
  tagUncitedFindings,
14085
+ topSkills,
13598
14086
  touchStream,
13599
14087
  trackAction,
13600
14088
  unfoldRange,