farai 0.2.7 → 0.2.8

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/cli/index.js CHANGED
@@ -5147,6 +5147,7 @@ function normalizeConfig(raw) {
5147
5147
  const context = contextConfig(raw.context);
5148
5148
  const lsp = lspConfig(raw.lsp);
5149
5149
  const web = webConfig(raw.web);
5150
+ const updates = updatesConfig(raw.updates);
5150
5151
  return {
5151
5152
  ...positiveInteger(raw.config_version ?? raw.configVersion) !== undefined ? {
5152
5153
  configVersion: positiveInteger(raw.config_version ?? raw.configVersion)
@@ -5206,8 +5207,29 @@ function normalizeConfig(raw) {
5206
5207
  } : {},
5207
5208
  ...web ? {
5208
5209
  web
5210
+ } : {},
5211
+ ...updates ? {
5212
+ updates
5213
+ } : {}
5214
+ };
5215
+ }
5216
+ function updatesConfig(value) {
5217
+ if (!isRecord2(value))
5218
+ return;
5219
+ const rawContentEnabled = value.content_enabled ?? value.contentEnabled;
5220
+ const contentManifestUrl = typeof (value.content_manifest_url ?? value.contentManifestUrl) === "string" ? String(value.content_manifest_url ?? value.contentManifestUrl).trim() : undefined;
5221
+ const config = {
5222
+ ...typeof rawContentEnabled === "boolean" ? {
5223
+ contentEnabled: rawContentEnabled
5224
+ } : {},
5225
+ ...contentManifestUrl ? {
5226
+ contentManifestUrl
5227
+ } : {},
5228
+ ...typeof value.prompt === "boolean" ? {
5229
+ prompt: value.prompt
5209
5230
  } : {}
5210
5231
  };
5232
+ return Object.keys(config).length ? config : undefined;
5211
5233
  }
5212
5234
  function webConfig(value) {
5213
5235
  if (!isRecord2(value))
@@ -5389,6 +5411,12 @@ function mergeConfig(base, over) {
5389
5411
  ...base.web,
5390
5412
  ...over.web
5391
5413
  }
5414
+ } : {},
5415
+ ...base.updates || over.updates ? {
5416
+ updates: {
5417
+ ...base.updates,
5418
+ ...over.updates
5419
+ }
5392
5420
  } : {}
5393
5421
  };
5394
5422
  }
@@ -5444,6 +5472,18 @@ function serializeConfigToml(config) {
5444
5472
  searxng_url: config.web.searxngUrl
5445
5473
  } : {}
5446
5474
  });
5475
+ if (config.updates && Object.keys(config.updates).length)
5476
+ emitTable(lines, ["updates"], {
5477
+ ...config.updates.contentEnabled === undefined ? {} : {
5478
+ content_enabled: config.updates.contentEnabled
5479
+ },
5480
+ ...config.updates.contentManifestUrl ? {
5481
+ content_manifest_url: config.updates.contentManifestUrl
5482
+ } : {},
5483
+ ...config.updates.prompt === undefined ? {} : {
5484
+ prompt: config.updates.prompt
5485
+ }
5486
+ });
5447
5487
  for (const [selection, limit] of Object.entries(config.modelLimits ?? {})) {
5448
5488
  emitTable(lines, ["model_limits", selection], {
5449
5489
  ...limit.contextWindow ? {
@@ -5606,6 +5646,10 @@ model = "big-pickle"
5606
5646
  mode = "explicit"
5607
5647
  tls = "relaxed"
5608
5648
 
5649
+ [updates]
5650
+ content_enabled = true
5651
+ prompt = true
5652
+
5609
5653
  [mcp_servers.mitmproxy-mcp]
5610
5654
  command = "mitmproxy-mcp"
5611
5655
  args = []
@@ -7419,8 +7463,8 @@ var init_process_output = __esm(() => {
7419
7463
 
7420
7464
  // src/version.ts
7421
7465
  function resolveFaraiVersion() {
7422
- if ("0.2.7")
7423
- return "0.2.7";
7466
+ if ("0.2.8")
7467
+ return "0.2.8";
7424
7468
  try {
7425
7469
  const parsed = JSON.parse(readBoundedFileTextSync(new URL("../package.json", import.meta.url), 1024 * 1024, "package metadata"));
7426
7470
  if (typeof parsed.version === "string" && parsed.version)
@@ -17293,11 +17337,112 @@ var init_memory_mark_failed = __esm(() => {
17293
17337
  };
17294
17338
  });
17295
17339
 
17340
+ // src/agent-content/paths.ts
17341
+ import { existsSync as existsSync8 } from "fs";
17342
+ import { isAbsolute as isAbsolute4, join as join13, resolve as resolve7 } from "path";
17343
+ function contentRoot() {
17344
+ const configured = process.env.FARAI_CONTENT_DIR?.trim();
17345
+ if (!configured)
17346
+ return join13(globalDataDir(), "content");
17347
+ if (!isAbsolute4(configured))
17348
+ throw new Error("farai_content_dir must be an absolute path");
17349
+ return resolve7(configured);
17350
+ }
17351
+ function contentVersionsDir() {
17352
+ return join13(contentRoot(), "versions");
17353
+ }
17354
+ function contentActivePath() {
17355
+ return join13(contentRoot(), "active.json");
17356
+ }
17357
+ function contentManifestCachePath() {
17358
+ return join13(contentRoot(), "manifest-cache.json");
17359
+ }
17360
+ function contentPreferencesPath() {
17361
+ return join13(contentRoot(), "preferences.json");
17362
+ }
17363
+ function contentLockPath() {
17364
+ return join13(contentRoot(), "update.lock");
17365
+ }
17366
+ function contentVersionDir(version) {
17367
+ if (!VERSION_PATTERN.test(version))
17368
+ throw new Error(`invalid content version: ${version}`);
17369
+ return join13(contentVersionsDir(), version);
17370
+ }
17371
+ function readActiveContent() {
17372
+ const path = contentActivePath();
17373
+ try {
17374
+ if (!existsSync8(path))
17375
+ return;
17376
+ ensurePrivateRegularFileIfExists(path, "active content pointer");
17377
+ const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, POINTER_MAX_BYTES, "active content pointer"));
17378
+ return parseActiveContent(parsed);
17379
+ } catch {
17380
+ return;
17381
+ }
17382
+ }
17383
+ function activeContentKnowledgePath() {
17384
+ const active = readActiveContent();
17385
+ if (!active?.knowledge)
17386
+ return;
17387
+ const path = join13(contentVersionDir(active.version), "knowledge.db");
17388
+ return existsSync8(path) ? path : undefined;
17389
+ }
17390
+ function activeContentSkillsDir() {
17391
+ const active = readActiveContent();
17392
+ if (!active?.skills)
17393
+ return;
17394
+ const path = join13(contentVersionDir(active.version), "skills");
17395
+ return existsSync8(path) ? path : undefined;
17396
+ }
17397
+ function ensureContentDirectories() {
17398
+ ensurePrivateDirectory(contentRoot(), "farai content directory");
17399
+ ensurePrivateDirectory(contentVersionsDir(), "farai content versions directory");
17400
+ }
17401
+ function parseActiveContent(value) {
17402
+ if (!value || typeof value !== "object" || Array.isArray(value))
17403
+ return;
17404
+ const record2 = value;
17405
+ if (record2.schemaVersion !== 1)
17406
+ return;
17407
+ if (typeof record2.version !== "string" || !VERSION_PATTERN.test(record2.version))
17408
+ return;
17409
+ if (!validDate(record2.generatedAt) || !validDate(record2.activatedAt))
17410
+ return;
17411
+ if (typeof record2.manifestUrl !== "string" || !record2.manifestUrl)
17412
+ return;
17413
+ if (typeof record2.knowledge !== "boolean" || typeof record2.skills !== "boolean")
17414
+ return;
17415
+ const previousVersion = typeof record2.previousVersion === "string" && VERSION_PATTERN.test(record2.previousVersion) ? record2.previousVersion : undefined;
17416
+ return {
17417
+ schemaVersion: 1,
17418
+ version: record2.version,
17419
+ generatedAt: record2.generatedAt,
17420
+ activatedAt: record2.activatedAt,
17421
+ manifestUrl: record2.manifestUrl,
17422
+ ...previousVersion ? {
17423
+ previousVersion
17424
+ } : {},
17425
+ knowledge: record2.knowledge,
17426
+ skills: record2.skills
17427
+ };
17428
+ }
17429
+ function validDate(value) {
17430
+ return typeof value === "string" && value.length > 0 && Number.isFinite(Date.parse(value));
17431
+ }
17432
+ var POINTER_MAX_BYTES, VERSION_PATTERN;
17433
+ var init_paths2 = __esm(() => {
17434
+ init_paths();
17435
+ init_private_path();
17436
+ init_file_read();
17437
+ POINTER_MAX_BYTES = 64 * 1024;
17438
+ VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
17439
+ });
17440
+
17296
17441
  // src/agent-skills/registry.ts
17297
17442
  import { createHash as createHash5 } from "crypto";
17298
- import { existsSync as existsSync8, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
17443
+ import { existsSync as existsSync9, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
17299
17444
  import { homedir as homedir4 } from "os";
17300
- import { delimiter, isAbsolute as isAbsolute4, join as join13, relative as relative4, resolve as resolve7, sep as sep2 } from "path";
17445
+ import { delimiter, isAbsolute as isAbsolute5, join as join14, relative as relative4, resolve as resolve8, sep as sep2 } from "path";
17301
17446
  function discoverSkills(options = {}) {
17302
17447
  const diagnostics = [];
17303
17448
  const selected = new Map;
@@ -17352,7 +17497,7 @@ function loadSkill(name, options = {}) {
17352
17497
  const resourcePath = normalizeResourcePath(options.resource);
17353
17498
  if (!resourcePath || !skill.resources.includes(resourcePath))
17354
17499
  return;
17355
- const absolute = resolve7(skill.directory, resourcePath);
17500
+ const absolute = resolve8(skill.directory, resourcePath);
17356
17501
  const canonical = realpathSync2(absolute);
17357
17502
  if (!inside(skill.directory, canonical))
17358
17503
  return;
@@ -17393,28 +17538,35 @@ function skillRoots(options) {
17393
17538
  source: "builtin",
17394
17539
  priority: 0
17395
17540
  }];
17541
+ const content = activeContentSkillsDir();
17542
+ if (content)
17543
+ roots.push({
17544
+ path: content,
17545
+ source: "content",
17546
+ priority: 5
17547
+ });
17396
17548
  if (options.includeUser !== false)
17397
17549
  roots.push({
17398
- path: join13(homedir4(), ".agents", "skills"),
17550
+ path: join14(homedir4(), ".agents", "skills"),
17399
17551
  source: "user",
17400
17552
  priority: 10
17401
17553
  });
17402
17554
  const environmentRoots = [...process.env.FARAI_SKILLS_DIR?.split(delimiter) ?? [], ...options.extraRoots ?? []].map((path) => path.trim()).filter(Boolean);
17403
17555
  for (const path of environmentRoots)
17404
17556
  roots.push({
17405
- path: resolve7(path),
17557
+ path: resolve8(path),
17406
17558
  source: "environment",
17407
17559
  priority: 20
17408
17560
  });
17409
17561
  if (options.workspace)
17410
17562
  roots.push({
17411
- path: join13(resolve7(options.workspace), ".agents", "skills"),
17563
+ path: join14(resolve8(options.workspace), ".agents", "skills"),
17412
17564
  source: "project",
17413
17565
  priority: 30
17414
17566
  });
17415
17567
  const seen = new Set;
17416
17568
  return roots.filter((root) => {
17417
- const key = resolve7(root.path);
17569
+ const key = resolve8(root.path);
17418
17570
  if (seen.has(key))
17419
17571
  return false;
17420
17572
  seen.add(key);
@@ -17422,7 +17574,7 @@ function skillRoots(options) {
17422
17574
  });
17423
17575
  }
17424
17576
  function scanRoot(root, diagnostics) {
17425
- if (!existsSync8(root.path))
17577
+ if (!existsSync9(root.path))
17426
17578
  return [];
17427
17579
  let entries;
17428
17580
  try {
@@ -17442,9 +17594,9 @@ function scanRoot(root, diagnostics) {
17442
17594
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
17443
17595
  if (!entry.isDirectory() && !entry.isSymbolicLink())
17444
17596
  continue;
17445
- const directory = join13(root.path, entry.name);
17446
- const file = join13(directory, "SKILL.md");
17447
- if (!existsSync8(file))
17597
+ const directory = join14(root.path, entry.name);
17598
+ const file = join14(directory, "SKILL.md");
17599
+ if (!existsSync9(file))
17448
17600
  continue;
17449
17601
  const parsed = parseSkill(file, directory, entry.name, root, diagnostics);
17450
17602
  if (parsed)
@@ -17453,8 +17605,8 @@ function scanRoot(root, diagnostics) {
17453
17605
  return skills;
17454
17606
  }
17455
17607
  function resolveBuiltinSkillDir() {
17456
- const candidates = [join13(import.meta.dir, "library"), join13(import.meta.dir, "..", "..", "src", "agent-skills", "library")];
17457
- return candidates.find((candidate) => existsSync8(candidate)) ?? candidates[0];
17608
+ const candidates = [join14(import.meta.dir, "library"), join14(import.meta.dir, "..", "..", "src", "agent-skills", "library")];
17609
+ return candidates.find((candidate) => existsSync9(candidate)) ?? candidates[0];
17458
17610
  }
17459
17611
  function parseSkill(file, directory, directoryName, root, diagnostics) {
17460
17612
  let raw;
@@ -17589,7 +17741,7 @@ function listResources(directory, diagnostics) {
17589
17741
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
17590
17742
  if (resources.length >= MAX_RESOURCES || entry.name.startsWith("."))
17591
17743
  continue;
17592
- const path = join13(current, entry.name);
17744
+ const path = join14(current, entry.name);
17593
17745
  let canonical;
17594
17746
  try {
17595
17747
  canonical = realpathSync2(path);
@@ -17600,7 +17752,7 @@ function listResources(directory, diagnostics) {
17600
17752
  continue;
17601
17753
  if (entry.isDirectory())
17602
17754
  visit(canonical);
17603
- else if ((entry.isFile() || entry.isSymbolicLink()) && canonical !== join13(directory, "SKILL.md")) {
17755
+ else if ((entry.isFile() || entry.isSymbolicLink()) && canonical !== join14(directory, "SKILL.md")) {
17604
17756
  resources.push(relative4(directory, canonical).split(sep2).join("/"));
17605
17757
  }
17606
17758
  }
@@ -17642,7 +17794,7 @@ function optionalScalar(value) {
17642
17794
  }
17643
17795
  function normalizeResourcePath(path) {
17644
17796
  const trimmed = path.trim().replaceAll("\\", "/");
17645
- if (!trimmed || isAbsolute4(trimmed) || trimmed.includes("\x00"))
17797
+ if (!trimmed || isAbsolute5(trimmed) || trimmed.includes("\x00"))
17646
17798
  return;
17647
17799
  const normalized = trimmed.split("/").filter((part) => part && part !== ".").join("/");
17648
17800
  if (!normalized || normalized.split("/").includes(".."))
@@ -17651,7 +17803,7 @@ function normalizeResourcePath(path) {
17651
17803
  }
17652
17804
  function inside(directory, path) {
17653
17805
  const rel = relative4(realpathSync2(directory), path);
17654
- return rel === "" || !rel.startsWith(`..${sep2}`) && rel !== ".." && !isAbsolute4(rel);
17806
+ return rel === "" || !rel.startsWith(`..${sep2}`) && rel !== ".." && !isAbsolute5(rel);
17655
17807
  }
17656
17808
  function compactText(value, max) {
17657
17809
  const compact = value.replace(/\s+/g, " ").trim();
@@ -17663,6 +17815,7 @@ function errorMessage(error) {
17663
17815
  var BUILTIN_DIR, MAX_SKILL_BYTES, RECOMMENDED_SKILL_BYTES, MAX_RESOURCE_BYTES, MAX_RESOURCES = 256, NAME_PATTERN;
17664
17816
  var init_registry2 = __esm(() => {
17665
17817
  init_file_read();
17818
+ init_paths2();
17666
17819
  BUILTIN_DIR = resolveBuiltinSkillDir();
17667
17820
  MAX_SKILL_BYTES = 64 * 1024;
17668
17821
  RECOMMENDED_SKILL_BYTES = 20 * 1024;
@@ -17798,8 +17951,8 @@ var init_global_config = __esm(() => {
17798
17951
 
17799
17952
  // src/agent-core/context-builder.ts
17800
17953
  import { execFileSync } from "child_process";
17801
- import { existsSync as existsSync9, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
17802
- import { isAbsolute as isAbsolute5, join as join14, normalize as normalize2, relative as relative5 } from "path";
17954
+ import { existsSync as existsSync10, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
17955
+ import { isAbsolute as isAbsolute6, join as join15, normalize as normalize2, relative as relative5 } from "path";
17803
17956
 
17804
17957
  class ContextBuilderCache {
17805
17958
  workspaceFiles = new Map;
@@ -17831,7 +17984,7 @@ class ContextBuilderCache {
17831
17984
  loadInstructionFragments(workspace, referencedPaths = []) {
17832
17985
  const dirs = instructionCandidateDirs(workspace, referencedPaths);
17833
17986
  const key = `${workspace}\x00${dirs.join("\x00")}`;
17834
- const fingerprint = dirs.flatMap((dir) => INSTRUCTION_FILENAMES.map((filename) => statFingerprint(join14(dir, filename)))).join("|");
17987
+ const fingerprint = dirs.flatMap((dir) => INSTRUCTION_FILENAMES.map((filename) => statFingerprint(join15(dir, filename)))).join("|");
17835
17988
  const cached = this.instructions.get(key);
17836
17989
  if (cached?.fingerprint === fingerprint)
17837
17990
  return cached.fragments;
@@ -17852,7 +18005,7 @@ class ContextBuilderCache {
17852
18005
  extractSymbols(workspace, file) {
17853
18006
  if (!/\.(?:[cm]?[jt]sx?|py|go|rs)$/i.test(file))
17854
18007
  return [];
17855
- const path = join14(workspace, file);
18008
+ const path = join15(workspace, file);
17856
18009
  const fingerprint = statFingerprint(path);
17857
18010
  const cached = this.symbols.get(path);
17858
18011
  if (cached?.fingerprint === fingerprint)
@@ -17913,7 +18066,7 @@ function collectWorkspaceFilesFromFs(workspace) {
17913
18066
  break;
17914
18067
  if (entry.name.startsWith(".") || FALLBACK_IGNORED_DIRECTORIES.has(entry.name))
17915
18068
  continue;
17916
- const absolute = join14(directory, entry.name);
18069
+ const absolute = join15(directory, entry.name);
17917
18070
  if (entry.isDirectory())
17918
18071
  pending.push(absolute);
17919
18072
  else if (entry.isFile())
@@ -18006,8 +18159,8 @@ function fragment(input) {
18006
18159
  }
18007
18160
  function readFirstInstructionFile(dir) {
18008
18161
  for (const filename of INSTRUCTION_FILENAMES) {
18009
- const path = join14(dir, filename);
18010
- if (!existsSync9(path))
18162
+ const path = join15(dir, filename);
18163
+ if (!existsSync10(path))
18011
18164
  continue;
18012
18165
  try {
18013
18166
  const body = readFileTextPrefixSync(path, PROJECT_INSTRUCTIONS_MAX_BYTES, "project instructions").text.trim();
@@ -18035,7 +18188,7 @@ function referencedInstructionDirs(workspace, referencedPaths) {
18035
18188
  parts.pop();
18036
18189
  let current = workspace;
18037
18190
  for (const part of parts) {
18038
- current = join14(current, part);
18191
+ current = join15(current, part);
18039
18192
  if (seen.has(current))
18040
18193
  continue;
18041
18194
  seen.add(current);
@@ -18048,7 +18201,7 @@ function instructionCandidateDirs(workspace, referencedPaths) {
18048
18201
  return [...new Set([...globalInstructionDirs(), workspace, ...referencedInstructionDirs(workspace, referencedPaths)])];
18049
18202
  }
18050
18203
  function workspaceFileFingerprint(workspace) {
18051
- return `${statFingerprint(workspace)}|${statFingerprint(join14(workspace, ".git", "index"))}`;
18204
+ return `${statFingerprint(workspace)}|${statFingerprint(join15(workspace, ".git", "index"))}`;
18052
18205
  }
18053
18206
  function statFingerprint(path) {
18054
18207
  try {
@@ -18064,7 +18217,7 @@ function validWorkspaceFiles(workspace, paths) {
18064
18217
  const rel = workspaceRelativeReference(workspace, value);
18065
18218
  if (!rel)
18066
18219
  continue;
18067
- const absolute = join14(workspace, rel);
18220
+ const absolute = join15(workspace, rel);
18068
18221
  try {
18069
18222
  if (statSync4(absolute).isFile())
18070
18223
  files.push(rel);
@@ -18078,9 +18231,9 @@ function workspaceRelativeReference(workspace, value) {
18078
18231
  return;
18079
18232
  const containerPrefix = `${normalize2("/workspace")}/`;
18080
18233
  const worktreeMatch = normalized.match(/^[/\\]worktrees[/\\][^/\\]+[/\\](.+)$/);
18081
- const absolute = normalized.startsWith(containerPrefix) ? normalize2(join14(workspace, normalized.slice(containerPrefix.length))) : worktreeMatch ? normalize2(join14(workspace, worktreeMatch[1])) : isAbsolute5(normalized) ? normalized : normalize2(join14(workspace, normalized.replace(/^\/+/, "")));
18234
+ const absolute = normalized.startsWith(containerPrefix) ? normalize2(join15(workspace, normalized.slice(containerPrefix.length))) : worktreeMatch ? normalize2(join15(workspace, worktreeMatch[1])) : isAbsolute6(normalized) ? normalized : normalize2(join15(workspace, normalized.replace(/^\/+/, "")));
18082
18235
  const rel = relative5(workspace, absolute);
18083
- if (!rel || rel.startsWith("..") || isAbsolute5(rel))
18236
+ if (!rel || rel.startsWith("..") || isAbsolute6(rel))
18084
18237
  return;
18085
18238
  return rel;
18086
18239
  }
@@ -18822,7 +18975,7 @@ class HostProcessBackend {
18822
18975
  }
18823
18976
  async runOnce(command, opts) {
18824
18977
  const started = Date.now();
18825
- return await new Promise((resolve8) => {
18978
+ return await new Promise((resolve9) => {
18826
18979
  const child = spawn4("bash", ["-lc", command], {
18827
18980
  cwd: this.cwd,
18828
18981
  stdio: ["pipe", "pipe", "pipe"],
@@ -18842,7 +18995,7 @@ class HostProcessBackend {
18842
18995
  const {
18843
18996
  sessionId
18844
18997
  } = sessions.register(child);
18845
- resolve8({
18998
+ resolve9({
18846
18999
  exitCode: null,
18847
19000
  stdout: stdout.text(),
18848
19001
  stderr: stderr.text(),
@@ -18882,7 +19035,7 @@ class HostProcessBackend {
18882
19035
  return;
18883
19036
  settled = true;
18884
19037
  stderr.push(error.message);
18885
- resolve8({
19038
+ resolve9({
18886
19039
  exitCode: 127,
18887
19040
  stdout: stdout.text(),
18888
19041
  stderr: stderr.text(),
@@ -18897,7 +19050,7 @@ class HostProcessBackend {
18897
19050
  if (converted || settled)
18898
19051
  return;
18899
19052
  settled = true;
18900
- resolve8({
19053
+ resolve9({
18901
19054
  exitCode,
18902
19055
  stdout: stdout.text(),
18903
19056
  stderr: stderr.text(),
@@ -19868,17 +20021,17 @@ var init_next_action = __esm(() => {
19868
20021
  });
19869
20022
 
19870
20023
  // src/agent-core/subagents/lanes.ts
19871
- import { existsSync as existsSync10 } from "fs";
19872
- import { join as join15 } from "path";
20024
+ import { existsSync as existsSync11 } from "fs";
20025
+ import { join as join16 } from "path";
19873
20026
  function laneConfigPaths(workspace) {
19874
20027
  if (false)
19875
20028
  ;
19876
- return [join15(localFaraiDir(), "agents.json"), join15(workspace, ".farai", "agents.json")];
20029
+ return [join16(localFaraiDir(), "agents.json"), join16(workspace, ".farai", "agents.json")];
19877
20030
  }
19878
20031
  function loadLanes(workspace) {
19879
20032
  const merged = new Map(BUILTIN_LANES.map((lane) => [lane.id, structuredClone(lane)]));
19880
20033
  for (const path of laneConfigPaths(workspace)) {
19881
- if (!existsSync10(path))
20034
+ if (!existsSync11(path))
19882
20035
  continue;
19883
20036
  try {
19884
20037
  const parsed = JSON.parse(readBoundedFileTextSync(path, LANE_CONFIG_MAX_BYTES, "agent lane config"));
@@ -20593,8 +20746,8 @@ class BrowserContextManager {
20593
20746
  const config = isolatedBrowserConfig(base, contextId);
20594
20747
  let resolveReady = () => {};
20595
20748
  let rejectReady = (_error) => {};
20596
- const ready = new Promise((resolve8, reject) => {
20597
- resolveReady = resolve8;
20749
+ const ready = new Promise((resolve9, reject) => {
20750
+ resolveReady = resolve9;
20598
20751
  rejectReady = reject;
20599
20752
  });
20600
20753
  ready.catch(() => {});
@@ -20787,9 +20940,9 @@ class AsyncMutex {
20787
20940
  this.locked = true;
20788
20941
  return Promise.resolve();
20789
20942
  }
20790
- return new Promise((resolve8, reject) => {
20943
+ return new Promise((resolve9, reject) => {
20791
20944
  const waiter = {
20792
- resolve: resolve8,
20945
+ resolve: resolve9,
20793
20946
  reject,
20794
20947
  ...signal ? {
20795
20948
  signal
@@ -20825,7 +20978,7 @@ async function waitForSignal(promise, signal) {
20825
20978
  return await promise;
20826
20979
  if (signal.aborted)
20827
20980
  throw signal.reason ?? new Error("Browser context operation cancelled");
20828
- return await new Promise((resolve8, reject) => {
20981
+ return await new Promise((resolve9, reject) => {
20829
20982
  const abort = () => {
20830
20983
  cleanup();
20831
20984
  reject(signal.reason ?? new Error("Browser context operation cancelled"));
@@ -20836,7 +20989,7 @@ async function waitForSignal(promise, signal) {
20836
20989
  });
20837
20990
  promise.then((value) => {
20838
20991
  cleanup();
20839
- resolve8(value);
20992
+ resolve9(value);
20840
20993
  }, (error) => {
20841
20994
  cleanup();
20842
20995
  reject(error);
@@ -22247,14 +22400,14 @@ var init_shared2 = __esm(() => {
22247
22400
  // src/agent-tools/web/web-fetch.ts
22248
22401
  import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
22249
22402
  import { randomUUID as randomUUID3 } from "crypto";
22250
- import { join as join16 } from "path";
22403
+ import { join as join17 } from "path";
22251
22404
  async function extractPdf(context, bytes) {
22252
- const directory = join16(context.workspace, ".farai", "tmp");
22405
+ const directory = join17(context.workspace, ".farai", "tmp");
22253
22406
  mkdirSync5(directory, {
22254
22407
  recursive: true
22255
22408
  });
22256
22409
  const name = `web-fetch-${randomUUID3()}.pdf`;
22257
- const path = join16(directory, name);
22410
+ const path = join17(directory, name);
22258
22411
  atomicWriteFile(path, bytes, 384);
22259
22412
  try {
22260
22413
  const backendPath = context.executionBackend?.kind === "host" ? path : `/workspace/.farai/tmp/${name}`;
@@ -23276,10 +23429,10 @@ var init_worktree = __esm(() => {
23276
23429
  });
23277
23430
 
23278
23431
  // src/agent-tools/services/mitmproxy/flows.ts
23279
- import { existsSync as existsSync11 } from "fs";
23432
+ import { existsSync as existsSync12 } from "fs";
23280
23433
  import { isIP as isIP2 } from "net";
23281
23434
  async function readProxyFlows(file, query = {}) {
23282
- if (!existsSync11(file))
23435
+ if (!existsSync12(file))
23283
23436
  return [];
23284
23437
  const limit = proxyFlowLimit(query.limit);
23285
23438
  const flows = [];
@@ -23311,7 +23464,7 @@ async function readProxyFlows(file, query = {}) {
23311
23464
  return flows;
23312
23465
  }
23313
23466
  async function readProxyFlowDetail(file, id2) {
23314
- if (!existsSync11(file))
23467
+ if (!existsSync12(file))
23315
23468
  return;
23316
23469
  let found;
23317
23470
  await forEachFileLine(file, {
@@ -24947,7 +25100,7 @@ function detailMatches(detail, options) {
24947
25100
  return !options.body || detail.text.toLowerCase().includes(options.body.toLowerCase());
24948
25101
  }
24949
25102
  async function waitForMailboxChange(client, timeoutMs, signal) {
24950
- await new Promise((resolve8, reject) => {
25103
+ await new Promise((resolve9, reject) => {
24951
25104
  let settled = false;
24952
25105
  const finish = (error) => {
24953
25106
  if (settled)
@@ -24959,7 +25112,7 @@ async function waitForMailboxChange(client, timeoutMs, signal) {
24959
25112
  if (error)
24960
25113
  reject(error);
24961
25114
  else
24962
- resolve8();
25115
+ resolve9();
24963
25116
  };
24964
25117
  const onAbort = () => finish(signal?.reason ?? new Error("email wait cancelled"));
24965
25118
  const onExists = () => finish();
@@ -24976,8 +25129,8 @@ async function waitForMailboxChange(client, timeoutMs, signal) {
24976
25129
  async function closeImap(client) {
24977
25130
  let timer;
24978
25131
  try {
24979
- const closed = await Promise.race([client.logout().then(() => true, () => false), new Promise((resolve8) => {
24980
- timer = setTimeout(() => resolve8(false), IMAP_LOGOUT_TIMEOUT_MS);
25132
+ const closed = await Promise.race([client.logout().then(() => true, () => false), new Promise((resolve9) => {
25133
+ timer = setTimeout(() => resolve9(false), IMAP_LOGOUT_TIMEOUT_MS);
24981
25134
  timer.unref?.();
24982
25135
  })]);
24983
25136
  if (!closed)
@@ -25001,7 +25154,7 @@ async function withAbort(promise, signal) {
25001
25154
  signal?.throwIfAborted();
25002
25155
  if (!signal)
25003
25156
  return await promise;
25004
- return await new Promise((resolve8, reject) => {
25157
+ return await new Promise((resolve9, reject) => {
25005
25158
  const onAbort = () => {
25006
25159
  signal.removeEventListener("abort", onAbort);
25007
25160
  reject(signal.reason ?? new Error("operation cancelled"));
@@ -25011,7 +25164,7 @@ async function withAbort(promise, signal) {
25011
25164
  });
25012
25165
  promise.then((value) => {
25013
25166
  signal.removeEventListener("abort", onAbort);
25014
- resolve8(value);
25167
+ resolve9(value);
25015
25168
  }, (error) => {
25016
25169
  signal.removeEventListener("abort", onAbort);
25017
25170
  reject(error);
@@ -25994,7 +26147,7 @@ function toActivity2(entry) {
25994
26147
  };
25995
26148
  }
25996
26149
  function delay2(ms, signal) {
25997
- return new Promise((resolve8, reject) => {
26150
+ return new Promise((resolve9, reject) => {
25998
26151
  let settled = false;
25999
26152
  const finish = (error) => {
26000
26153
  if (settled)
@@ -26005,7 +26158,7 @@ function delay2(ms, signal) {
26005
26158
  if (error)
26006
26159
  reject(error);
26007
26160
  else
26008
- resolve8();
26161
+ resolve9();
26009
26162
  };
26010
26163
  const abort = () => finish(signal?.reason ?? new Error("email wait cancelled"));
26011
26164
  const timer = setTimeout(() => finish(), ms);
@@ -27541,8 +27694,8 @@ var init_model_profiles = __esm(() => {
27541
27694
  });
27542
27695
 
27543
27696
  // src/agent-core/model-catalog.ts
27544
- import { existsSync as existsSync12 } from "fs";
27545
- import { dirname as dirname7, join as join17 } from "path";
27697
+ import { existsSync as existsSync13 } from "fs";
27698
+ import { dirname as dirname7, join as join18 } from "path";
27546
27699
  async function buildModelCatalog(workspace, profiles = loadModelProfiles(workspace)) {
27547
27700
  const providers = [];
27548
27701
  const models = [];
@@ -28118,7 +28271,7 @@ async function fetchModelsDevCatalog(fetchImpl = globalThis.fetch, timeoutMs = M
28118
28271
  function readCachedModelsDevCatalog() {
28119
28272
  try {
28120
28273
  const path = modelsDevCachePath();
28121
- if (!existsSync12(path))
28274
+ if (!existsSync13(path))
28122
28275
  return;
28123
28276
  ensurePrivateDirectory(dirname7(path), "model cache directory");
28124
28277
  ensurePrivateRegularFileIfExists(path, "models.dev cache");
@@ -28170,7 +28323,7 @@ function ensureConcrete(resolved) {
28170
28323
  return resolved;
28171
28324
  }
28172
28325
  function modelsDevCachePath() {
28173
- return join17(globalDataDir(), "cache", "models-dev.json");
28326
+ return join18(globalDataDir(), "cache", "models-dev.json");
28174
28327
  }
28175
28328
  var DEFAULT_PROVIDER_ID = "default", OPENCODE_PROVIDER_ID, OPENCODE_DEFAULT_MODEL_ID, OPENCODE_PUBLIC_API_KEY, MODELS_DEV_URL = "https://models.dev/api.json", MODELS_DEV_CACHE_TTL_MS, MODELS_DEV_STALE_TTL_MS, MODELS_DEV_FETCH_TIMEOUT_MS = 4000, MODELS_DEV_MAX_BYTES, RECENT_MODEL_LIMIT = 12, modelsDevRefreshes;
28176
28329
  var init_model_catalog = __esm(() => {
@@ -30505,17 +30658,17 @@ var init_compaction = __esm(() => {
30505
30658
  });
30506
30659
 
30507
30660
  // src/agent-core/hooks/host.ts
30508
- import { existsSync as existsSync13 } from "fs";
30509
- import { join as join18 } from "path";
30661
+ import { existsSync as existsSync14 } from "fs";
30662
+ import { join as join19 } from "path";
30510
30663
  function hookConfigPaths(workspace) {
30511
30664
  if (false)
30512
30665
  ;
30513
- return [join18(localFaraiDir(), "hooks.json"), join18(workspace, ".farai", "hooks.json")];
30666
+ return [join19(localFaraiDir(), "hooks.json"), join19(workspace, ".farai", "hooks.json")];
30514
30667
  }
30515
30668
  function loadHooks(workspace) {
30516
30669
  const hooks = [];
30517
30670
  for (const path of hookConfigPaths(workspace)) {
30518
- if (!existsSync13(path))
30671
+ if (!existsSync14(path))
30519
30672
  continue;
30520
30673
  try {
30521
30674
  const parsed = JSON.parse(readBoundedFileTextSync(path, HOOK_CONFIG_MAX_BYTES, "hook config"));
@@ -30639,7 +30792,7 @@ class SubagentGate {
30639
30792
  idle() {
30640
30793
  if (this.active === 0 && this.queue.length === 0)
30641
30794
  return Promise.resolve();
30642
- return new Promise((resolve8) => this.idleResolvers.add(resolve8));
30795
+ return new Promise((resolve9) => this.idleResolvers.add(resolve9));
30643
30796
  }
30644
30797
  async run(work, signal) {
30645
30798
  const release = await this.acquire(signal);
@@ -30651,13 +30804,13 @@ class SubagentGate {
30651
30804
  }
30652
30805
  }
30653
30806
  acquire(signal) {
30654
- return new Promise((resolve8, reject) => {
30807
+ return new Promise((resolve9, reject) => {
30655
30808
  if (signal?.aborted) {
30656
30809
  reject(signal.reason ?? new Error("subagent task cancelled before start"));
30657
30810
  return;
30658
30811
  }
30659
30812
  const waiter = {
30660
- resolve: resolve8,
30813
+ resolve: resolve9,
30661
30814
  reject,
30662
30815
  ...signal ? {
30663
30816
  signal
@@ -30704,8 +30857,8 @@ class SubagentGate {
30704
30857
  notifyIdle() {
30705
30858
  if (this.active !== 0 || this.queue.length !== 0)
30706
30859
  return;
30707
- for (const resolve8 of this.idleResolvers)
30708
- resolve8();
30860
+ for (const resolve9 of this.idleResolvers)
30861
+ resolve9();
30709
30862
  this.idleResolvers.clear();
30710
30863
  }
30711
30864
  }
@@ -30719,10 +30872,10 @@ class SessionActor {
30719
30872
  run(work) {
30720
30873
  if (this.closed)
30721
30874
  return Promise.reject(new Error("Session actor is closed"));
30722
- return new Promise((resolve8, reject) => {
30875
+ return new Promise((resolve9, reject) => {
30723
30876
  this.queue.push({
30724
30877
  work,
30725
- resolve: (value) => resolve8(value),
30878
+ resolve: (value) => resolve9(value),
30726
30879
  reject
30727
30880
  });
30728
30881
  this.drain();
@@ -30734,7 +30887,7 @@ class SessionActor {
30734
30887
  idle() {
30735
30888
  if (!this.running && this.queue.length === 0)
30736
30889
  return Promise.resolve();
30737
- return new Promise((resolve8) => this.idleResolvers.add(resolve8));
30890
+ return new Promise((resolve9) => this.idleResolvers.add(resolve9));
30738
30891
  }
30739
30892
  close() {
30740
30893
  if (this.closed)
@@ -30767,8 +30920,8 @@ class SessionActor {
30767
30920
  resolveIdle() {
30768
30921
  if (this.running || this.queue.length > 0)
30769
30922
  return;
30770
- for (const resolve8 of this.idleResolvers)
30771
- resolve8();
30923
+ for (const resolve9 of this.idleResolvers)
30924
+ resolve9();
30772
30925
  this.idleResolvers.clear();
30773
30926
  }
30774
30927
  }
@@ -32882,7 +33035,7 @@ function rebuildFtsIndexes(db) {
32882
33035
  var KNOWLEDGE_SCHEMA_VERSION = 2;
32883
33036
 
32884
33037
  // src/agent-knowledge/store.ts
32885
- import { existsSync as existsSync14 } from "fs";
33038
+ import { existsSync as existsSync15 } from "fs";
32886
33039
  import { Database as Database3 } from "bun:sqlite";
32887
33040
 
32888
33041
  class KnowledgeStore {
@@ -32891,7 +33044,7 @@ class KnowledgeStore {
32891
33044
  this.create = create;
32892
33045
  }
32893
33046
  static openIfExists(path) {
32894
- if (!existsSync14(path))
33047
+ if (!existsSync15(path))
32895
33048
  return;
32896
33049
  const store = new KnowledgeStore(path);
32897
33050
  try {
@@ -33466,24 +33619,28 @@ var init_store = __esm(() => {
33466
33619
  });
33467
33620
 
33468
33621
  // src/agent-knowledge/paths.ts
33469
- import { join as join19 } from "path";
33622
+ import { join as join20 } from "path";
33470
33623
  function knowledgeDbPath() {
33471
- return join19(localFaraiDir(), "knowledge.db");
33624
+ return activeContentKnowledgePath() ?? legacyKnowledgeDbPath();
33625
+ }
33626
+ function legacyKnowledgeDbPath() {
33627
+ return join20(localFaraiDir(), "knowledge.db");
33472
33628
  }
33473
33629
  function knowledgeRoot() {
33474
- return process.env.FARAI_KNOWLEDGE_DIR ?? join19(localFaraiDir(), "knowledge");
33630
+ return process.env.FARAI_KNOWLEDGE_DIR ?? join20(localFaraiDir(), "knowledge");
33475
33631
  }
33476
33632
  function packsDir() {
33477
- return join19(knowledgeRoot(), "packs");
33633
+ return join20(knowledgeRoot(), "packs");
33478
33634
  }
33479
33635
  function taxonomyDir() {
33480
- return join19(knowledgeRoot(), "taxonomy");
33636
+ return join20(knowledgeRoot(), "taxonomy");
33481
33637
  }
33482
33638
  function cacheDir() {
33483
- return join19(localFaraiDir(), "knowledge-cache");
33639
+ return join20(localFaraiDir(), "knowledge-cache");
33484
33640
  }
33485
- var init_paths2 = __esm(() => {
33641
+ var init_paths3 = __esm(() => {
33486
33642
  init_config();
33643
+ init_paths2();
33487
33644
  });
33488
33645
 
33489
33646
  // src/agent-core/model-pricing.ts
@@ -33517,14 +33674,14 @@ function nonNegative(value) {
33517
33674
  }
33518
33675
 
33519
33676
  // src/session-catalog.ts
33520
- import { existsSync as existsSync15, readdirSync as readdirSync5, rmSync as rmSync2 } from "fs";
33521
- import { join as join20, resolve as resolve8 } from "path";
33677
+ import { existsSync as existsSync16, readdirSync as readdirSync5, rmSync as rmSync2 } from "fs";
33678
+ import { join as join21, resolve as resolve9 } from "path";
33522
33679
  function recordSessionLocation(session) {
33523
33680
  const directory = catalogDirectory();
33524
33681
  ensurePrivateDirectory(directory, "session catalog directory");
33525
33682
  const entry = {
33526
33683
  id: session.id,
33527
- workspace: resolve8(session.workspace),
33684
+ workspace: resolve9(session.workspace),
33528
33685
  ...session.title ? {
33529
33686
  title: session.title
33530
33687
  } : {},
@@ -33533,13 +33690,13 @@ function recordSessionLocation(session) {
33533
33690
  } : {},
33534
33691
  updatedAt: session.updatedAt
33535
33692
  };
33536
- const path = join20(directory, `${session.id}.json`);
33693
+ const path = join21(directory, `${session.id}.json`);
33537
33694
  ensurePrivateRegularFileIfExists(path, "session catalog entry");
33538
33695
  atomicWriteFile(path, `${JSON.stringify(entry)}
33539
33696
  `, 384);
33540
33697
  }
33541
33698
  function removeSessionLocation(sessionId) {
33542
- rmSync2(join20(catalogDirectory(), `${sessionId}.json`), {
33699
+ rmSync2(join21(catalogDirectory(), `${sessionId}.json`), {
33543
33700
  force: true
33544
33701
  });
33545
33702
  }
@@ -33559,7 +33716,7 @@ function resolveSessionLocation(query) {
33559
33716
  }
33560
33717
  function listSessionLocations() {
33561
33718
  const directory = catalogDirectory();
33562
- if (!existsSync15(directory))
33719
+ if (!existsSync16(directory))
33563
33720
  return [];
33564
33721
  try {
33565
33722
  ensurePrivateDirectory(directory, "session catalog directory");
@@ -33568,7 +33725,7 @@ function listSessionLocations() {
33568
33725
  }
33569
33726
  return readdirSync5(directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
33570
33727
  try {
33571
- const path = join20(directory, name);
33728
+ const path = join21(directory, name);
33572
33729
  ensurePrivateRegularFileIfExists(path, "session catalog entry");
33573
33730
  const value = JSON.parse(readBoundedFileTextSyncNoFollow(path, SESSION_CATALOG_ENTRY_MAX_BYTES, "session catalog entry"));
33574
33731
  if (typeof value.id !== "string" || typeof value.workspace !== "string" || typeof value.updatedAt !== "string")
@@ -33590,10 +33747,10 @@ function listSessionLocations() {
33590
33747
  }).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
33591
33748
  }
33592
33749
  function catalogDirectory() {
33593
- return join20(localFaraiDir(), "sessions");
33750
+ return join21(localFaraiDir(), "sessions");
33594
33751
  }
33595
33752
  function sessionDatabaseExists(workspace) {
33596
- return existsSync15(join20(workspace, ".farai", "farai.db"));
33753
+ return existsSync16(join21(workspace, ".farai", "farai.db"));
33597
33754
  }
33598
33755
  var SESSION_CATALOG_ENTRY_MAX_BYTES;
33599
33756
  var init_session_catalog = __esm(() => {
@@ -33629,7 +33786,7 @@ class SessionUserInputCoordinator {
33629
33786
  expiresAt
33630
33787
  } : {}
33631
33788
  };
33632
- return new Promise((resolve9, reject) => {
33789
+ return new Promise((resolve10, reject) => {
33633
33790
  const detach = () => signal?.removeEventListener("abort", abort);
33634
33791
  const abort = () => {
33635
33792
  if (this.pending.get(sessionId)?.request.id !== request.id)
@@ -33646,7 +33803,7 @@ class SessionUserInputCoordinator {
33646
33803
  request,
33647
33804
  resolve: (answer) => {
33648
33805
  detach();
33649
- resolve9(answer);
33806
+ resolve10(answer);
33650
33807
  },
33651
33808
  reject: (error) => {
33652
33809
  detach();
@@ -33979,7 +34136,7 @@ class ToolExecutionGate {
33979
34136
  idle() {
33980
34137
  if (this.states.size === 0)
33981
34138
  return Promise.resolve();
33982
- return new Promise((resolve9) => this.idleResolvers.add(resolve9));
34139
+ return new Promise((resolve10) => this.idleResolvers.add(resolve10));
33983
34140
  }
33984
34141
  async run(key, parallel, fn, signal) {
33985
34142
  const release = await this.acquire(key, parallel ? "read" : "write", signal);
@@ -33991,7 +34148,7 @@ class ToolExecutionGate {
33991
34148
  }
33992
34149
  }
33993
34150
  acquire(key, mode, signal) {
33994
- return new Promise((resolve9, reject) => {
34151
+ return new Promise((resolve10, reject) => {
33995
34152
  if (signal?.aborted) {
33996
34153
  reject(signal.reason ?? new Error("tool gate acquisition cancelled"));
33997
34154
  return;
@@ -34004,7 +34161,7 @@ class ToolExecutionGate {
34004
34161
  this.states.set(key, state);
34005
34162
  const waiter = {
34006
34163
  mode,
34007
- resolve: resolve9,
34164
+ resolve: resolve10,
34008
34165
  reject,
34009
34166
  ...signal ? {
34010
34167
  signal
@@ -34061,8 +34218,8 @@ class ToolExecutionGate {
34061
34218
  if (state.activeReaders === 0 && !state.activeWriter && state.queue.length === 0 && this.states.get(key) === state) {
34062
34219
  this.states.delete(key);
34063
34220
  if (this.states.size === 0) {
34064
- for (const resolve9 of this.idleResolvers)
34065
- resolve9();
34221
+ for (const resolve10 of this.idleResolvers)
34222
+ resolve10();
34066
34223
  this.idleResolvers.clear();
34067
34224
  }
34068
34225
  }
@@ -34109,7 +34266,7 @@ function abortablePromise(promise, signal) {
34109
34266
  return promise;
34110
34267
  if (signal.aborted)
34111
34268
  return Promise.reject(abortReason(signal));
34112
- return new Promise((resolve9, reject) => {
34269
+ return new Promise((resolve10, reject) => {
34113
34270
  const cleanup = () => signal.removeEventListener("abort", onAbort);
34114
34271
  const onAbort = () => {
34115
34272
  cleanup();
@@ -34120,7 +34277,7 @@ function abortablePromise(promise, signal) {
34120
34277
  });
34121
34278
  promise.then((value) => {
34122
34279
  cleanup();
34123
- resolve9(value);
34280
+ resolve10(value);
34124
34281
  }, (error) => {
34125
34282
  cleanup();
34126
34283
  reject(error);
@@ -34505,8 +34662,8 @@ var init_tool_call_journal = () => {};
34505
34662
 
34506
34663
  // src/agent-core/runtime.ts
34507
34664
  import { createHash as createHash9 } from "crypto";
34508
- import { existsSync as existsSync16, mkdirSync as mkdirSync6, realpathSync as realpathSync3 } from "fs";
34509
- import { isAbsolute as isAbsolute6, join as join21, relative as relative7 } from "path";
34665
+ import { existsSync as existsSync17, mkdirSync as mkdirSync6, realpathSync as realpathSync3 } from "fs";
34666
+ import { isAbsolute as isAbsolute7, join as join22, relative as relative7 } from "path";
34510
34667
  function assertProviderToolIndex2(index, max) {
34511
34668
  if (!Number.isInteger(index) || index < 0 || index >= max)
34512
34669
  throw new Error(`provider tool call index must be between 0 and ${max - 1}`);
@@ -34552,7 +34709,7 @@ class AgentRuntime {
34552
34709
  this.workspace = workspace;
34553
34710
  this.inheritConfig = options.inheritConfig !== false;
34554
34711
  const config = this.inheritConfig ? loadConfig(workspace) : {};
34555
- this.store = new SqliteStore(join21(workspace, ".farai"));
34712
+ this.store = new SqliteStore(join22(workspace, ".farai"));
34556
34713
  this.toolCalls = new ToolCallJournal(this.store, (sessionId, type, payload) => this.event(sessionId, type, payload));
34557
34714
  this.knowledgeEnabled = options.enableKnowledge !== false;
34558
34715
  this.hooksEnabled = options.enableHooks !== false;
@@ -35374,16 +35531,16 @@ class AgentRuntime {
35374
35531
  }
35375
35532
  waitForSteering(sessionId) {
35376
35533
  let settled = false;
35377
- let resolve9;
35534
+ let resolve10;
35378
35535
  const promise = new Promise((done) => {
35379
- resolve9 = done;
35536
+ resolve10 = done;
35380
35537
  });
35381
35538
  const wake = () => {
35382
35539
  if (settled)
35383
35540
  return;
35384
35541
  settled = true;
35385
35542
  this.steeringWaiters.get(sessionId)?.delete(wake);
35386
- resolve9();
35543
+ resolve10();
35387
35544
  };
35388
35545
  const waiters = this.steeringWaiters.get(sessionId) ?? new Set;
35389
35546
  waiters.add(wake);
@@ -35731,12 +35888,12 @@ class AgentRuntime {
35731
35888
  return {
35732
35889
  markdown
35733
35890
  };
35734
- const dir = join21(this.workspace, ".farai", "reports");
35891
+ const dir = join22(this.workspace, ".farai", "reports");
35735
35892
  mkdirSync6(dir, {
35736
35893
  recursive: true
35737
35894
  });
35738
35895
  const stamp = new Date().toISOString().slice(0, 10);
35739
- const path = join21(dir, `${sessionId}-${stamp}.md`);
35896
+ const path = join22(dir, `${sessionId}-${stamp}.md`);
35740
35897
  atomicWriteFile(path, markdown, 384);
35741
35898
  return {
35742
35899
  markdown,
@@ -37836,7 +37993,7 @@ This completion is already terminal and was delivered automatically. Do not call
37836
37993
  }
37837
37994
  if (entries.length === 0 || entries.some((entry) => !entry.running) || Date.now() - started >= timeoutMs)
37838
37995
  return entries;
37839
- await new Promise((resolve9) => setTimeout(resolve9, 200));
37996
+ await new Promise((resolve10) => setTimeout(resolve10, 200));
37840
37997
  }
37841
37998
  },
37842
37999
  message: (childSessionId, text2) => {
@@ -37896,15 +38053,15 @@ This completion is already terminal and was delivered automatically. Do not call
37896
38053
  const root = (await runHostGit(this.workspace, ["rev-parse", "--show-toplevel"])).trim();
37897
38054
  if (realpathSync3(root) !== realpathSync3(this.workspace))
37898
38055
  throw new Error(`Farai workspace is not the Git repository root: ${root}`);
37899
- const worktreesRoot = join21(this.workspace, ".farai", "worktrees");
38056
+ const worktreesRoot = join22(this.workspace, ".farai", "worktrees");
37900
38057
  mkdirSync6(worktreesRoot, {
37901
38058
  recursive: true
37902
38059
  });
37903
- const path = join21(worktreesRoot, safeName);
38060
+ const path = join22(worktreesRoot, safeName);
37904
38061
  const registered = await registeredWorktree(this.workspace, path);
37905
- if (existsSync16(path) && !registered)
38062
+ if (existsSync17(path) && !registered)
37906
38063
  throw new Error(`worktree path already exists but is not a registered Git worktree: ${path}`);
37907
- if (!existsSync16(path) && registered)
38064
+ if (!existsSync17(path) && registered)
37908
38065
  throw new Error(`worktree registration exists but its directory is missing: ${path}; repair or prune it before re-entry`);
37909
38066
  if (registered) {
37910
38067
  if (ref || branch)
@@ -37954,9 +38111,9 @@ This completion is already terminal and was delivered automatically. Do not call
37954
38111
  if (current.workspace === this.workspace)
37955
38112
  throw new Error("session is not inside an isolated worktree");
37956
38113
  this.assertWorkspaceTransitionIdle(session.id);
37957
- const worktreesRoot = join21(this.workspace, ".farai", "worktrees");
38114
+ const worktreesRoot = join22(this.workspace, ".farai", "worktrees");
37958
38115
  const managedPath = relative7(worktreesRoot, current.workspace);
37959
- if (!managedPath || managedPath.startsWith("..") || isAbsolute6(managedPath)) {
38116
+ if (!managedPath || managedPath.startsWith("..") || isAbsolute7(managedPath)) {
37960
38117
  throw new Error(`refusing to leave an unmanaged worktree: ${current.workspace}`);
37961
38118
  }
37962
38119
  if (remove) {
@@ -38838,16 +38995,16 @@ function plannerRetryState(error, attempt, safeToReplay) {
38838
38995
  function abortableSleep(ms, signal) {
38839
38996
  if (signal?.aborted)
38840
38997
  return Promise.resolve();
38841
- return new Promise((resolve9) => {
38998
+ return new Promise((resolve10) => {
38842
38999
  const cleanup = () => signal?.removeEventListener("abort", onAbort);
38843
39000
  const onAbort = () => {
38844
39001
  clearTimeout(timer);
38845
39002
  cleanup();
38846
- resolve9();
39003
+ resolve10();
38847
39004
  };
38848
39005
  const timer = setTimeout(() => {
38849
39006
  cleanup();
38850
- resolve9();
39007
+ resolve10();
38851
39008
  }, ms);
38852
39009
  signal?.addEventListener("abort", onAbort, {
38853
39010
  once: true
@@ -38897,18 +39054,18 @@ function shutdownGracePeriod(value) {
38897
39054
  return Math.max(0, value);
38898
39055
  }
38899
39056
  function waitForShutdownFinalization(finalization, gracePeriodMs) {
38900
- return new Promise((resolve9, reject) => {
39057
+ return new Promise((resolve10, reject) => {
38901
39058
  let settled = false;
38902
39059
  const timer = setTimeout(() => {
38903
39060
  settled = true;
38904
- resolve9(false);
39061
+ resolve10(false);
38905
39062
  }, gracePeriodMs);
38906
39063
  finalization.then(() => {
38907
39064
  if (settled)
38908
39065
  return;
38909
39066
  settled = true;
38910
39067
  clearTimeout(timer);
38911
- resolve9(true);
39068
+ resolve10(true);
38912
39069
  }, (error) => {
38913
39070
  if (settled)
38914
39071
  return;
@@ -39035,7 +39192,7 @@ var init_runtime = __esm(() => {
39035
39192
  init_retry();
39036
39193
  init_reasoning_summary();
39037
39194
  init_store();
39038
- init_paths2();
39195
+ init_paths3();
39039
39196
  init_session_catalog();
39040
39197
  init_session_user_input();
39041
39198
  init_tool_execution_control();
@@ -39086,37 +39243,796 @@ var init_branding = __esm(() => {
39086
39243
  `);
39087
39244
  });
39088
39245
 
39089
- // src/agent-knowledge/pack.ts
39090
- import { closeSync as closeSync4, existsSync as existsSync17, fsyncSync as fsyncSync2, lstatSync as lstatSync4, mkdirSync as mkdirSync7, openSync as openSync4, readdirSync as readdirSync6, renameSync as renameSync3, rmSync as rmSync3, writeSync } from "fs";
39246
+ // src/agent-core/semver.ts
39247
+ function compareSemver(left, right) {
39248
+ const a = parseSemver(left);
39249
+ const b = parseSemver(right);
39250
+ if (!a || !b)
39251
+ return 0;
39252
+ for (let index = 0;index < 3; index += 1) {
39253
+ const delta = a.core[index] - b.core[index];
39254
+ if (delta !== 0)
39255
+ return delta < 0 ? -1 : 1;
39256
+ }
39257
+ if (a.prerelease.length === 0 || b.prerelease.length === 0) {
39258
+ if (a.prerelease.length === b.prerelease.length)
39259
+ return 0;
39260
+ return a.prerelease.length === 0 ? 1 : -1;
39261
+ }
39262
+ const length = Math.max(a.prerelease.length, b.prerelease.length);
39263
+ for (let index = 0;index < length; index += 1) {
39264
+ const aPart = a.prerelease[index];
39265
+ const bPart = b.prerelease[index];
39266
+ if (aPart === undefined || bPart === undefined)
39267
+ return aPart === undefined ? -1 : 1;
39268
+ if (aPart === bPart)
39269
+ continue;
39270
+ const aNumber = numericIdentifier(aPart);
39271
+ const bNumber = numericIdentifier(bPart);
39272
+ if (aNumber !== undefined && bNumber !== undefined)
39273
+ return aNumber < bNumber ? -1 : 1;
39274
+ if (aNumber !== undefined || bNumber !== undefined)
39275
+ return aNumber !== undefined ? -1 : 1;
39276
+ return aPart < bPart ? -1 : 1;
39277
+ }
39278
+ return 0;
39279
+ }
39280
+ function isSemver(value) {
39281
+ return parseSemver(value) !== undefined;
39282
+ }
39283
+ function parseSemver(value) {
39284
+ const match = value.trim().match(/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
39285
+ if (!match)
39286
+ return;
39287
+ return {
39288
+ core: [Number(match[1]), Number(match[2]), Number(match[3])],
39289
+ prerelease: match[4]?.split(".") ?? []
39290
+ };
39291
+ }
39292
+ function numericIdentifier(value) {
39293
+ if (!/^(0|[1-9]\d*)$/.test(value))
39294
+ return;
39295
+ return Number(value);
39296
+ }
39297
+
39298
+ // src/agent-content/manifest.ts
39299
+ async function fetchContentManifest(manifestUrl, options = {}) {
39300
+ const resolved = new URL(manifestUrl);
39301
+ let parsed;
39302
+ if (resolved.protocol === "file:") {
39303
+ parsed = JSON.parse(await readBoundedFileText(resolved, CONTENT_MANIFEST_MAX_BYTES, "content manifest"));
39304
+ } else {
39305
+ if (resolved.protocol !== "https:")
39306
+ throw new Error("content manifest must use https or file");
39307
+ const controller = new AbortController;
39308
+ const timer = setTimeout(() => controller.abort(), Math.max(1, options.timeoutMs ?? 5000));
39309
+ timer.unref?.();
39310
+ try {
39311
+ const response = await (options.fetcher ?? fetch)(resolved, {
39312
+ headers: {
39313
+ accept: "application/json"
39314
+ },
39315
+ signal: controller.signal
39316
+ });
39317
+ if (!response.ok) {
39318
+ await discardResponseBody(response);
39319
+ throw new Error(`content manifest returned ${response.status}`);
39320
+ }
39321
+ parsed = await readBoundedResponseJson(response, CONTENT_MANIFEST_MAX_BYTES, "content manifest");
39322
+ } finally {
39323
+ clearTimeout(timer);
39324
+ }
39325
+ }
39326
+ return parseContentManifest(parsed, resolved);
39327
+ }
39328
+ function parseContentManifest(value, baseUrl) {
39329
+ if (!value || typeof value !== "object" || Array.isArray(value))
39330
+ throw new Error("content manifest must be an object");
39331
+ const record3 = value;
39332
+ if (record3.schemaVersion !== CONTENT_MANIFEST_SCHEMA_VERSION)
39333
+ throw new Error(`unsupported content manifest schema: ${String(record3.schemaVersion)}`);
39334
+ if (typeof record3.contentVersion !== "string" || !CONTENT_VERSION_PATTERN.test(record3.contentVersion))
39335
+ throw new Error("invalid content version");
39336
+ if (typeof record3.generatedAt !== "string" || !Number.isFinite(Date.parse(record3.generatedAt)))
39337
+ throw new Error("invalid content generatedAt");
39338
+ const minFaraiVersion = optionalString2(record3.minFaraiVersion, 128, "minFaraiVersion");
39339
+ if (minFaraiVersion && !isSemver(minFaraiVersion))
39340
+ throw new Error("invalid content minFaraiVersion");
39341
+ const releaseNotes = optionalString2(record3.releaseNotes, 4096, "releaseNotes");
39342
+ const knowledge = artifact(record3.knowledge, baseUrl, "knowledge");
39343
+ const skills = artifact(record3.skills, baseUrl, "skills");
39344
+ return {
39345
+ schemaVersion: 1,
39346
+ contentVersion: record3.contentVersion,
39347
+ generatedAt: new Date(record3.generatedAt).toISOString(),
39348
+ ...minFaraiVersion ? {
39349
+ minFaraiVersion
39350
+ } : {},
39351
+ ...releaseNotes ? {
39352
+ releaseNotes
39353
+ } : {},
39354
+ ...knowledge ? {
39355
+ knowledge
39356
+ } : {},
39357
+ ...skills ? {
39358
+ skills
39359
+ } : {}
39360
+ };
39361
+ }
39362
+ function artifact(value, baseUrl, label) {
39363
+ if (value === undefined)
39364
+ return;
39365
+ if (!value || typeof value !== "object" || Array.isArray(value))
39366
+ throw new Error(`${label} artifact must be an object`);
39367
+ const record3 = value;
39368
+ if (typeof record3.url !== "string" || !record3.url || record3.url.length > 2048)
39369
+ throw new Error(`${label} artifact has an invalid url`);
39370
+ const url = new URL(record3.url, baseUrl);
39371
+ if (baseUrl.protocol === "https:" && url.protocol !== "https:")
39372
+ throw new Error(`${label} artifact must use https when the manifest is remote`);
39373
+ if (url.protocol !== "https:" && url.protocol !== "file:")
39374
+ throw new Error(`${label} artifact must use https or file`);
39375
+ if (typeof record3.sha256 !== "string" || !SHA256_PATTERN.test(record3.sha256))
39376
+ throw new Error(`${label} artifact has an invalid sha256`);
39377
+ if (!Number.isSafeInteger(record3.size) || Number(record3.size) < 1 || Number(record3.size) > 2147483648)
39378
+ throw new Error(`${label} artifact has an invalid size`);
39379
+ const schemaVersion = record3.schemaVersion === undefined ? undefined : Number.isSafeInteger(record3.schemaVersion) && Number(record3.schemaVersion) > 0 ? Number(record3.schemaVersion) : undefined;
39380
+ if (record3.schemaVersion !== undefined && schemaVersion === undefined)
39381
+ throw new Error(`${label} artifact has an invalid schemaVersion`);
39382
+ return {
39383
+ url: url.href,
39384
+ sha256: record3.sha256,
39385
+ size: Number(record3.size),
39386
+ ...schemaVersion ? {
39387
+ schemaVersion
39388
+ } : {}
39389
+ };
39390
+ }
39391
+ function optionalString2(value, maxLength, label) {
39392
+ if (value === undefined)
39393
+ return;
39394
+ if (typeof value !== "string" || !value.trim() || value.length > maxLength)
39395
+ throw new Error(`invalid content ${label}`);
39396
+ return value.trim();
39397
+ }
39398
+ var CONTENT_MANIFEST_SCHEMA_VERSION = 1, CONTENT_MANIFEST_MAX_BYTES, DEFAULT_CONTENT_MANIFEST_URL = "https://github.com/pajarori/farai-data/releases/latest/download/manifest.json", CONTENT_VERSION_PATTERN, SHA256_PATTERN;
39399
+ var init_manifest = __esm(() => {
39400
+ init_http_response();
39401
+ init_file_read();
39402
+ CONTENT_MANIFEST_MAX_BYTES = 256 * 1024;
39403
+ CONTENT_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
39404
+ SHA256_PATTERN = /^[a-f0-9]{64}$/;
39405
+ });
39406
+
39407
+ // src/agent-content/updater.ts
39408
+ var exports_updater = {};
39409
+ __export(exports_updater, {
39410
+ rollbackContentUpdate: () => rollbackContentUpdate,
39411
+ isContentVersionDismissed: () => isContentVersionDismissed,
39412
+ dismissContentVersion: () => dismissContentVersion,
39413
+ contentStatus: () => contentStatus,
39414
+ checkContentUpdate: () => checkContentUpdate,
39415
+ applyContentUpdate: () => applyContentUpdate,
39416
+ CONTENT_UPDATE_TIMEOUT_MS: () => CONTENT_UPDATE_TIMEOUT_MS,
39417
+ CONTENT_MANIFEST_CACHE_TTL_MS: () => CONTENT_MANIFEST_CACHE_TTL_MS
39418
+ });
39091
39419
  import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
39092
- import { join as join22 } from "path";
39420
+ import { closeSync as closeSync4, existsSync as existsSync18, lstatSync as lstatSync4, mkdirSync as mkdirSync7, openSync as openSync4, readSync as readSync2, readdirSync as readdirSync6, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync6, unlinkSync as unlinkSync6, writeSync } from "fs";
39421
+ import { dirname as dirname9, join as join23 } from "path";
39422
+ import { fileURLToPath as fileURLToPath2 } from "url";
39423
+ async function checkContentUpdate(options = {}) {
39424
+ const config = loadConfig(options.workspace);
39425
+ const manifestUrl = options.manifestUrl ?? process.env.FARAI_CONTENT_MANIFEST_URL ?? config.updates?.contentManifestUrl ?? DEFAULT_CONTENT_MANIFEST_URL;
39426
+ if (contentUpdateDisabled(config.updates?.contentEnabled))
39427
+ return {
39428
+ state: "disabled",
39429
+ manifestUrl,
39430
+ fromCache: false
39431
+ };
39432
+ const now = options.now ?? Date.now();
39433
+ const cached = readManifestCache();
39434
+ const cacheMatches = cached?.manifestUrl === manifestUrl;
39435
+ let manifest;
39436
+ let fromCache = false;
39437
+ if (!options.force && cacheMatches && cached && isFresh(cached.checkedAt, now)) {
39438
+ if (!cached.manifest)
39439
+ return {
39440
+ state: "error",
39441
+ manifestUrl,
39442
+ fromCache: true,
39443
+ ...cached.error ? {
39444
+ error: cached.error
39445
+ } : {}
39446
+ };
39447
+ manifest = cached.manifest;
39448
+ fromCache = true;
39449
+ } else {
39450
+ try {
39451
+ manifest = await fetchContentManifest(manifestUrl, {
39452
+ ...options.fetcher ? {
39453
+ fetcher: options.fetcher
39454
+ } : {},
39455
+ timeoutMs: options.timeoutMs ?? CONTENT_UPDATE_TIMEOUT_MS
39456
+ });
39457
+ writeManifestCache({
39458
+ checkedAt: now,
39459
+ manifestUrl,
39460
+ manifest
39461
+ });
39462
+ } catch (error) {
39463
+ const message = errorMessage6(error);
39464
+ if (cacheMatches && cached?.manifest) {
39465
+ manifest = cached.manifest;
39466
+ fromCache = true;
39467
+ writeManifestCache({
39468
+ checkedAt: now,
39469
+ manifestUrl,
39470
+ manifest,
39471
+ error: message
39472
+ });
39473
+ } else {
39474
+ writeManifestCache({
39475
+ checkedAt: now,
39476
+ manifestUrl,
39477
+ error: message
39478
+ });
39479
+ return {
39480
+ state: "error",
39481
+ manifestUrl,
39482
+ fromCache: false,
39483
+ error: message
39484
+ };
39485
+ }
39486
+ }
39487
+ }
39488
+ const active = readActiveContent();
39489
+ if (!manifest || !manifest.knowledge && !manifest.skills)
39490
+ return {
39491
+ state: "unavailable",
39492
+ manifestUrl,
39493
+ fromCache,
39494
+ ...active ? {
39495
+ active
39496
+ } : {},
39497
+ ...manifest ? {
39498
+ manifest
39499
+ } : {}
39500
+ };
39501
+ if (manifest.minFaraiVersion && isSemver(manifest.minFaraiVersion) && compareSemver(FARAI_VERSION, manifest.minFaraiVersion) < 0) {
39502
+ return {
39503
+ state: "incompatible",
39504
+ manifestUrl,
39505
+ fromCache,
39506
+ manifest,
39507
+ ...active ? {
39508
+ active
39509
+ } : {}
39510
+ };
39511
+ }
39512
+ if (!isNewerManifest(manifest, active))
39513
+ return {
39514
+ state: "up_to_date",
39515
+ manifestUrl,
39516
+ fromCache,
39517
+ manifest,
39518
+ ...active ? {
39519
+ active
39520
+ } : {}
39521
+ };
39522
+ return {
39523
+ state: "update_available",
39524
+ manifestUrl,
39525
+ fromCache,
39526
+ manifest,
39527
+ ...active ? {
39528
+ active
39529
+ } : {}
39530
+ };
39531
+ }
39532
+ async function applyContentUpdate(manifest, manifestUrl, options = {}) {
39533
+ if (!manifest.knowledge && !manifest.skills)
39534
+ throw new Error("content manifest has no artifacts");
39535
+ ensureContentDirectories();
39536
+ const release = acquireLock();
39537
+ const stage = join23(contentVersionsDir(), `.staging-${process.pid}-${randomUUID4()}`);
39538
+ const finalPath = contentVersionDir(manifest.contentVersion);
39539
+ try {
39540
+ mkdirSync7(stage, {
39541
+ recursive: true,
39542
+ mode: 448
39543
+ });
39544
+ ensurePrivateDirectory(stage, "content staging directory");
39545
+ let knowledge = false;
39546
+ let skills = false;
39547
+ if (manifest.knowledge) {
39548
+ const path = join23(stage, "knowledge.db");
39549
+ await downloadArtifact(manifest.knowledge, path, options);
39550
+ validateKnowledge(path, manifest.knowledge.schemaVersion);
39551
+ knowledge = true;
39552
+ }
39553
+ if (manifest.skills) {
39554
+ if (manifest.skills.size > CONTENT_SKILLS_MAX_BYTES)
39555
+ throw new Error("skills artifact is too large");
39556
+ const archive = join23(stage, "skills.tar.gz");
39557
+ await downloadArtifact(manifest.skills, archive, options);
39558
+ await extractSkills(archive, join23(stage, "skills"));
39559
+ rmSync3(archive, {
39560
+ force: true
39561
+ });
39562
+ skills = true;
39563
+ }
39564
+ atomicWriteFile(join23(stage, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
39565
+ `, 384);
39566
+ ensurePrivateRegularFileIfExists(join23(stage, "manifest.json"), "staged content manifest");
39567
+ if (existsSync18(finalPath)) {
39568
+ const existing = join23(finalPath, "manifest.json");
39569
+ if (existsSync18(existing)) {
39570
+ const current = parseStoredManifest(existing);
39571
+ if (JSON.stringify(current) !== JSON.stringify(manifest))
39572
+ throw new Error(`content version already exists with different metadata: ${manifest.contentVersion}`);
39573
+ rmSync3(stage, {
39574
+ recursive: true,
39575
+ force: true
39576
+ });
39577
+ } else {
39578
+ rmSync3(finalPath, {
39579
+ recursive: true,
39580
+ force: true
39581
+ });
39582
+ renameSync3(stage, finalPath);
39583
+ }
39584
+ } else {
39585
+ renameSync3(stage, finalPath);
39586
+ }
39587
+ ensurePrivateDirectory(finalPath, "content version directory");
39588
+ const previous = readActiveContent();
39589
+ const pointer = {
39590
+ schemaVersion: 1,
39591
+ version: manifest.contentVersion,
39592
+ generatedAt: manifest.generatedAt,
39593
+ activatedAt: new Date().toISOString(),
39594
+ manifestUrl,
39595
+ ...previous && previous.version !== manifest.contentVersion ? {
39596
+ previousVersion: previous.version
39597
+ } : {},
39598
+ knowledge,
39599
+ skills
39600
+ };
39601
+ atomicWriteFile(contentActivePath(), `${JSON.stringify(pointer, null, 2)}
39602
+ `, 384);
39603
+ syncDirectory(contentVersionsDir());
39604
+ pruneVersions(pointer);
39605
+ return {
39606
+ version: pointer.version,
39607
+ ...pointer.previousVersion ? {
39608
+ previousVersion: pointer.previousVersion
39609
+ } : {},
39610
+ knowledge,
39611
+ skills,
39612
+ path: finalPath
39613
+ };
39614
+ } finally {
39615
+ if (existsSync18(stage))
39616
+ rmSync3(stage, {
39617
+ recursive: true,
39618
+ force: true
39619
+ });
39620
+ release();
39621
+ }
39622
+ }
39623
+ function rollbackContentUpdate() {
39624
+ ensureContentDirectories();
39625
+ const release = acquireLock();
39626
+ try {
39627
+ const active = readActiveContent();
39628
+ if (!active?.previousVersion)
39629
+ throw new Error("no previous content version is available");
39630
+ const previousPath = contentVersionDir(active.previousVersion);
39631
+ if (!existsSync18(previousPath))
39632
+ throw new Error(`previous content version is missing: ${active.previousVersion}`);
39633
+ const manifest = parseStoredManifest(join23(previousPath, "manifest.json"));
39634
+ const next = {
39635
+ schemaVersion: 1,
39636
+ version: active.previousVersion,
39637
+ generatedAt: manifest.generatedAt,
39638
+ activatedAt: new Date().toISOString(),
39639
+ manifestUrl: active.manifestUrl,
39640
+ previousVersion: active.version,
39641
+ knowledge: Boolean(manifest.knowledge && existsSync18(join23(previousPath, "knowledge.db"))),
39642
+ skills: Boolean(manifest.skills && existsSync18(join23(previousPath, "skills")))
39643
+ };
39644
+ atomicWriteFile(contentActivePath(), `${JSON.stringify(next, null, 2)}
39645
+ `, 384);
39646
+ return {
39647
+ version: next.version,
39648
+ previousVersion: next.previousVersion,
39649
+ knowledge: next.knowledge,
39650
+ skills: next.skills,
39651
+ path: previousPath
39652
+ };
39653
+ } finally {
39654
+ release();
39655
+ }
39656
+ }
39657
+ function contentStatus() {
39658
+ ensureContentDirectories();
39659
+ const active = readActiveContent();
39660
+ const versions = readdirVersions();
39661
+ const knowledgePath = activeContentKnowledgePath();
39662
+ const skillsPath = activeContentSkillsDir();
39663
+ return {
39664
+ ...active ? {
39665
+ active
39666
+ } : {},
39667
+ ...knowledgePath ? {
39668
+ knowledgePath
39669
+ } : {},
39670
+ ...skillsPath ? {
39671
+ skillsPath
39672
+ } : {},
39673
+ versions
39674
+ };
39675
+ }
39676
+ function isContentVersionDismissed(version) {
39677
+ try {
39678
+ const value = JSON.parse(readBoundedFileTextSyncNoFollow(contentPreferencesPath(), CONTENT_PREFERENCES_MAX_BYTES, "content preferences"));
39679
+ return value.dismissedVersion === version;
39680
+ } catch {
39681
+ return false;
39682
+ }
39683
+ }
39684
+ function dismissContentVersion(version) {
39685
+ ensureContentDirectories();
39686
+ atomicWriteFile(contentPreferencesPath(), `${JSON.stringify({
39687
+ dismissedVersion: version
39688
+ })}
39689
+ `, 384);
39690
+ }
39691
+ function contentUpdateDisabled(configured) {
39692
+ if (configured === false)
39693
+ return true;
39694
+ return [process.env.FARAI_DISABLE_CONTENT_UPDATE, process.env.FARAI_DISABLE_UPDATE_CHECK].some((value) => value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes");
39695
+ }
39696
+ function isNewerManifest(manifest, active) {
39697
+ if (!active)
39698
+ return true;
39699
+ if (manifest.contentVersion === active.version)
39700
+ return false;
39701
+ const generated = Date.parse(manifest.generatedAt);
39702
+ const activeGenerated = Date.parse(active.generatedAt);
39703
+ if (Number.isFinite(generated) && Number.isFinite(activeGenerated))
39704
+ return generated > activeGenerated;
39705
+ return manifest.contentVersion > active.version;
39706
+ }
39707
+ function readManifestCache() {
39708
+ try {
39709
+ ensureContentDirectories();
39710
+ const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(contentManifestCachePath(), CONTENT_MANIFEST_MAX_BYTES * 2, "content manifest cache"));
39711
+ if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt) || typeof parsed.manifestUrl !== "string" || !parsed.manifestUrl)
39712
+ return;
39713
+ const manifest = parsed.manifest ? parseContentManifest(parsed.manifest, new URL(parsed.manifestUrl)) : undefined;
39714
+ const error = typeof parsed.error === "string" && parsed.error ? parsed.error : undefined;
39715
+ if (!manifest && !error)
39716
+ return;
39717
+ return {
39718
+ checkedAt: parsed.checkedAt,
39719
+ manifestUrl: parsed.manifestUrl,
39720
+ ...manifest ? {
39721
+ manifest
39722
+ } : {},
39723
+ ...error ? {
39724
+ error
39725
+ } : {}
39726
+ };
39727
+ } catch {
39728
+ return;
39729
+ }
39730
+ }
39731
+ function writeManifestCache(cache) {
39732
+ try {
39733
+ ensureContentDirectories();
39734
+ atomicWriteFile(contentManifestCachePath(), `${JSON.stringify(cache)}
39735
+ `, 384);
39736
+ } catch {}
39737
+ }
39738
+ function isFresh(checkedAt, now) {
39739
+ const age = now - checkedAt;
39740
+ return age >= 0 && age < CONTENT_MANIFEST_CACHE_TTL_MS;
39741
+ }
39742
+ function acquireLock() {
39743
+ const path = contentLockPath();
39744
+ ensureContentDirectories();
39745
+ try {
39746
+ const descriptor = openSync4(path, "wx", 384);
39747
+ try {
39748
+ writeSync(descriptor, JSON.stringify({
39749
+ pid: process.pid,
39750
+ createdAt: Date.now()
39751
+ }));
39752
+ } finally {
39753
+ closeSync4(descriptor);
39754
+ }
39755
+ cleanupStagingDirectories();
39756
+ return () => {
39757
+ try {
39758
+ unlinkSync6(path);
39759
+ } catch {}
39760
+ };
39761
+ } catch (error) {
39762
+ if (!isAlreadyExists(error) || !staleLock(path))
39763
+ throw new Error("another farai content update is already running");
39764
+ try {
39765
+ unlinkSync6(path);
39766
+ } catch {
39767
+ throw new Error("another farai content update is already running");
39768
+ }
39769
+ return acquireLock();
39770
+ }
39771
+ }
39772
+ function staleLock(path) {
39773
+ try {
39774
+ const stats = statSync6(path);
39775
+ const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, 4 * 1024, "content update lock"));
39776
+ if (typeof parsed.pid === "number" && parsed.pid > 0) {
39777
+ try {
39778
+ process.kill(parsed.pid, 0);
39779
+ return false;
39780
+ } catch (error) {
39781
+ if (!isNoSuchProcess(error))
39782
+ return false;
39783
+ }
39784
+ }
39785
+ return Date.now() - stats.mtimeMs >= CONTENT_LOCK_STALE_MS || typeof parsed.pid === "number";
39786
+ } catch {
39787
+ return true;
39788
+ }
39789
+ }
39790
+ function cleanupStagingDirectories() {
39791
+ for (const entry of readdirSync6(contentVersionsDir())) {
39792
+ if (!entry.startsWith(".staging-"))
39793
+ continue;
39794
+ try {
39795
+ rmSync3(join23(contentVersionsDir(), entry), {
39796
+ recursive: true,
39797
+ force: true
39798
+ });
39799
+ } catch {}
39800
+ }
39801
+ }
39802
+ async function downloadArtifact(artifact2, destination, options) {
39803
+ if (artifact2.size > CONTENT_ARTIFACT_MAX_BYTES)
39804
+ throw new Error("content artifact is too large");
39805
+ const url = new URL(artifact2.url);
39806
+ mkdirSync7(dirname9(destination), {
39807
+ recursive: true,
39808
+ mode: 448
39809
+ });
39810
+ let bytes;
39811
+ if (url.protocol === "file:") {
39812
+ const source = Bun.file(url);
39813
+ const sourcePath = fileURLToPath2(url);
39814
+ const stats = lstatSync4(sourcePath);
39815
+ if (!stats.isFile())
39816
+ throw new Error("content artifact file must be a regular file");
39817
+ if (stats.size !== artifact2.size)
39818
+ throw new Error(`content artifact size mismatch: expected ${artifact2.size}, received ${stats.size}`);
39819
+ await Bun.write(destination, source);
39820
+ bytes = stats.size;
39821
+ } else {
39822
+ if (url.protocol !== "https:")
39823
+ throw new Error("content artifact must use https or file");
39824
+ const controller = new AbortController;
39825
+ const timer = setTimeout(() => controller.abort(), Math.max(1, options.timeoutMs ?? CONTENT_UPDATE_TIMEOUT_MS));
39826
+ timer.unref?.();
39827
+ let descriptor;
39828
+ try {
39829
+ const response = await (options.fetcher ?? fetch)(url, {
39830
+ signal: controller.signal
39831
+ });
39832
+ if (!response.ok || !response.body) {
39833
+ try {
39834
+ await response.body?.cancel();
39835
+ } catch {}
39836
+ throw new Error(`content artifact returned ${response.status}`);
39837
+ }
39838
+ const declared = Number(response.headers.get("content-length"));
39839
+ if (Number.isSafeInteger(declared) && declared !== artifact2.size)
39840
+ throw new Error(`content artifact size mismatch: expected ${artifact2.size}, received ${declared}`);
39841
+ descriptor = openSync4(destination, "wx", 384);
39842
+ const reader = response.body.getReader();
39843
+ bytes = 0;
39844
+ let completed = false;
39845
+ try {
39846
+ for (;; ) {
39847
+ const next = await reader.read();
39848
+ if (next.done) {
39849
+ completed = true;
39850
+ break;
39851
+ }
39852
+ bytes += next.value.byteLength;
39853
+ if (bytes > artifact2.size)
39854
+ throw new Error("content artifact exceeded declared size");
39855
+ writeSync(descriptor, next.value);
39856
+ }
39857
+ } finally {
39858
+ if (!completed)
39859
+ reader.cancel().catch(() => {
39860
+ return;
39861
+ });
39862
+ reader.releaseLock();
39863
+ }
39864
+ closeSync4(descriptor);
39865
+ descriptor = undefined;
39866
+ } finally {
39867
+ clearTimeout(timer);
39868
+ if (descriptor !== undefined)
39869
+ try {
39870
+ closeSync4(descriptor);
39871
+ } catch {}
39872
+ }
39873
+ }
39874
+ if (bytes !== artifact2.size)
39875
+ throw new Error(`content artifact size mismatch: expected ${artifact2.size}, received ${bytes}`);
39876
+ const digest2 = hashFile(destination);
39877
+ if (digest2 !== artifact2.sha256)
39878
+ throw new Error(`content artifact checksum mismatch for ${url.href}`);
39879
+ ensurePrivateRegularFileIfExists(destination, "downloaded content artifact");
39880
+ }
39881
+ function validateKnowledge(path, schemaVersion) {
39882
+ ensurePrivateSqlitePath(path, "staged knowledge database");
39883
+ if (schemaVersion !== undefined && schemaVersion !== KNOWLEDGE_SCHEMA_VERSION)
39884
+ throw new Error(`unsupported knowledge schema: ${schemaVersion}`);
39885
+ const store = KnowledgeStore.openIfExists(path);
39886
+ if (!store)
39887
+ throw new Error("staged knowledge database could not be opened");
39888
+ try {
39889
+ const integrity = store.verifyIntegrity();
39890
+ if (!integrity.ok)
39891
+ throw new Error(`staged knowledge database failed integrity: ${integrity.issues.map((issue) => `${issue.kind}=${issue.count}`).join(", ")}`);
39892
+ } finally {
39893
+ store.close();
39894
+ }
39895
+ }
39896
+ async function extractSkills(archive, destination) {
39897
+ const listing = await spawnTar(["-tzf", archive]);
39898
+ if (Buffer.byteLength(listing, "utf8") > CONTENT_SKILLS_LISTING_MAX_BYTES)
39899
+ throw new Error("skills archive listing is too large");
39900
+ const listingEntries = listing.split(/\r?\n/).map((item) => item.trim()).filter(Boolean);
39901
+ if (listingEntries.length > CONTENT_SKILLS_MAX_ENTRIES)
39902
+ throw new Error("skills archive contains too many entries");
39903
+ for (const entry of listingEntries) {
39904
+ if (entry.startsWith("/") || entry.split("/").includes("..") || entry.includes("\\"))
39905
+ throw new Error("skills archive contains an unsafe path");
39906
+ }
39907
+ mkdirSync7(destination, {
39908
+ recursive: true,
39909
+ mode: 448
39910
+ });
39911
+ await spawnTar(["-xzf", archive, "-C", destination]);
39912
+ const extractedEntries = [...walk(destination)];
39913
+ if (extractedEntries.some((path) => lstatSync4(path).isSymbolicLink()))
39914
+ throw new Error("skills archive contains a symbolic link");
39915
+ if (!extractedEntries.some((path) => path.endsWith("/SKILL.md")))
39916
+ throw new Error("skills archive contains no skills");
39917
+ }
39918
+ async function spawnTar(args) {
39919
+ const proc = Bun.spawn(["tar", ...args], {
39920
+ stdout: "pipe",
39921
+ stderr: "pipe"
39922
+ });
39923
+ const [stdout, stderr, code] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
39924
+ if (code !== 0)
39925
+ throw new Error(`skills archive operation failed${stderr.trim() ? `: ${stderr.trim()}` : ""}`);
39926
+ return stdout;
39927
+ }
39928
+ function* walk(root) {
39929
+ const entries = readdirSync6(root).sort();
39930
+ for (const entry of entries) {
39931
+ const path = join23(root, entry);
39932
+ const stat = lstatSync4(path);
39933
+ yield path;
39934
+ if (stat.isDirectory())
39935
+ yield* walk(path);
39936
+ }
39937
+ }
39938
+ function hashFile(path) {
39939
+ const descriptor = openSync4(path, "r");
39940
+ const hash = createHash10("sha256");
39941
+ const buffer = Buffer.allocUnsafe(64 * 1024);
39942
+ try {
39943
+ for (;; ) {
39944
+ const count2 = readSync2(descriptor, buffer, 0, buffer.length, null);
39945
+ if (count2 === 0)
39946
+ break;
39947
+ hash.update(buffer.subarray(0, count2));
39948
+ }
39949
+ return hash.digest("hex");
39950
+ } finally {
39951
+ closeSync4(descriptor);
39952
+ }
39953
+ }
39954
+ function parseStoredManifest(path) {
39955
+ ensurePrivateRegularFileIfExists(path, "stored content manifest");
39956
+ const raw = JSON.parse(readBoundedFileTextSyncNoFollow(path, CONTENT_MANIFEST_MAX_BYTES, "stored content manifest"));
39957
+ return parseContentManifest(raw, new URL("file:///stored/manifest.json"));
39958
+ }
39959
+ function pruneVersions(active) {
39960
+ const keep = new Set([active.version, active.previousVersion].filter((value) => Boolean(value)));
39961
+ for (const version of readdirVersions()) {
39962
+ if (keep.has(version))
39963
+ continue;
39964
+ try {
39965
+ rmSync3(contentVersionDir(version), {
39966
+ recursive: true,
39967
+ force: true
39968
+ });
39969
+ } catch {}
39970
+ }
39971
+ }
39972
+ function readdirVersions() {
39973
+ try {
39974
+ return readdirSync6(contentVersionsDir()).sort().filter((entry) => !entry.startsWith(".") && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(entry));
39975
+ } catch {
39976
+ return [];
39977
+ }
39978
+ }
39979
+ function errorMessage6(error) {
39980
+ return error instanceof Error ? error.message : String(error);
39981
+ }
39982
+ function isAlreadyExists(error) {
39983
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "EEXIST");
39984
+ }
39985
+ function isNoSuchProcess(error) {
39986
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ESRCH");
39987
+ }
39988
+ var CONTENT_MANIFEST_CACHE_TTL_MS, CONTENT_UPDATE_TIMEOUT_MS = 8000, CONTENT_ARTIFACT_MAX_BYTES = 2147483648, CONTENT_SKILLS_MAX_BYTES, CONTENT_SKILLS_MAX_ENTRIES = 8192, CONTENT_SKILLS_LISTING_MAX_BYTES, CONTENT_LOCK_STALE_MS, CONTENT_PREFERENCES_MAX_BYTES;
39989
+ var init_updater = __esm(() => {
39990
+ init_version();
39991
+ init_config();
39992
+ init_atomic_file();
39993
+ init_private_path();
39994
+ init_file_read();
39995
+ init_store();
39996
+ init_paths2();
39997
+ init_manifest();
39998
+ CONTENT_MANIFEST_CACHE_TTL_MS = 20 * 60 * 60 * 1000;
39999
+ CONTENT_SKILLS_MAX_BYTES = 256 * 1024 * 1024;
40000
+ CONTENT_SKILLS_LISTING_MAX_BYTES = 4 * 1024 * 1024;
40001
+ CONTENT_LOCK_STALE_MS = 15 * 60 * 1000;
40002
+ CONTENT_PREFERENCES_MAX_BYTES = 32 * 1024;
40003
+ });
40004
+
40005
+ // src/agent-knowledge/pack.ts
40006
+ import { closeSync as closeSync5, existsSync as existsSync19, fsyncSync as fsyncSync2, lstatSync as lstatSync5, mkdirSync as mkdirSync8, openSync as openSync5, readdirSync as readdirSync7, renameSync as renameSync4, rmSync as rmSync4, writeSync as writeSync2 } from "fs";
40007
+ import { createHash as createHash11, randomUUID as randomUUID5 } from "crypto";
40008
+ import { join as join24 } from "path";
39093
40009
  function packDir(meta) {
39094
40010
  assertPackPathPart(meta.id, "knowledge pack id");
39095
40011
  assertPackPathPart(meta.pin, "knowledge pack pin");
39096
- return join22(packsDir(), `${meta.id}@${meta.pin.slice(0, 12)}`);
40012
+ return join24(packsDir(), `${meta.id}@${meta.pin.slice(0, 12)}`);
39097
40013
  }
39098
40014
  function writePack(meta, records, entities) {
39099
40015
  const root = packsDir();
39100
40016
  ensurePrivateDirectory(root, "knowledge pack directory");
39101
40017
  const dir = packDir(meta);
39102
- const temporary = `${dir}.tmp-${process.pid}-${randomUUID4()}`;
39103
- mkdirSync7(temporary, {
40018
+ const temporary = `${dir}.tmp-${process.pid}-${randomUUID5()}`;
40019
+ mkdirSync8(temporary, {
39104
40020
  mode: 448
39105
40021
  });
39106
40022
  try {
39107
- atomicWriteFile(join22(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
40023
+ atomicWriteFile(join24(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
39108
40024
  `, 384);
39109
- writeJsonl(join22(temporary, "records.jsonl"), records, "knowledge records");
39110
- writeJsonl(join22(temporary, "entities.jsonl"), entities, "knowledge entities");
40025
+ writeJsonl(join24(temporary, "records.jsonl"), records, "knowledge records");
40026
+ writeJsonl(join24(temporary, "entities.jsonl"), entities, "knowledge entities");
39111
40027
  if (realDirectoryExists(dir, "knowledge pack"))
39112
- rmSync3(dir, {
40028
+ rmSync4(dir, {
39113
40029
  recursive: true,
39114
40030
  force: true
39115
40031
  });
39116
- renameSync3(temporary, dir);
40032
+ renameSync4(temporary, dir);
39117
40033
  syncDirectory(root);
39118
40034
  } catch (error) {
39119
- rmSync3(temporary, {
40035
+ rmSync4(temporary, {
39120
40036
  recursive: true,
39121
40037
  force: true
39122
40038
  });
@@ -39126,19 +40042,19 @@ function writePack(meta, records, entities) {
39126
40042
  }
39127
40043
  function listPacks() {
39128
40044
  const root = packsDir();
39129
- if (!existsSync17(root))
40045
+ if (!existsSync19(root))
39130
40046
  return [];
39131
40047
  assertRealDirectory(root, "knowledge pack directory");
39132
- const entries = readdirSync6(root);
40048
+ const entries = readdirSync7(root);
39133
40049
  if (entries.length > PACK_DIRECTORY_MAX_COUNT)
39134
40050
  throw new Error(`knowledge pack directory exceeded ${PACK_DIRECTORY_MAX_COUNT} entries`);
39135
40051
  const out = [];
39136
40052
  for (const entry of entries) {
39137
- const dir = join22(root, entry);
40053
+ const dir = join24(root, entry);
39138
40054
  try {
39139
40055
  assertRealDirectory(dir, "knowledge pack");
39140
- const metaPath = join22(dir, "meta.json");
39141
- if (!existsSync17(metaPath))
40056
+ const metaPath = join24(dir, "meta.json");
40057
+ if (!existsSync19(metaPath))
39142
40058
  continue;
39143
40059
  const meta = JSON.parse(readBoundedFileTextSyncNoFollow(metaPath, PACK_META_MAX_BYTES, "knowledge pack metadata"));
39144
40060
  if (!validPackMeta(meta))
@@ -39162,14 +40078,14 @@ function latestPacks() {
39162
40078
  }
39163
40079
  function readRecords(dir) {
39164
40080
  assertRealDirectory(dir, "knowledge pack");
39165
- return readJsonl(join22(dir, "records.jsonl"), "knowledge records");
40081
+ return readJsonl(join24(dir, "records.jsonl"), "knowledge records");
39166
40082
  }
39167
40083
  function readEntities(dir) {
39168
40084
  assertRealDirectory(dir, "knowledge pack");
39169
- return readJsonl(join22(dir, "entities.jsonl"), "knowledge entities");
40085
+ return readJsonl(join24(dir, "entities.jsonl"), "knowledge entities");
39170
40086
  }
39171
40087
  function readJsonl(path, label) {
39172
- if (!existsSync17(path))
40088
+ if (!existsSync19(path))
39173
40089
  return [];
39174
40090
  const out = [];
39175
40091
  forEachFileLineSync(path, {
@@ -39190,7 +40106,7 @@ function readJsonl(path, label) {
39190
40106
  return out;
39191
40107
  }
39192
40108
  function writeJsonl(path, values, label) {
39193
- const descriptor = openSync4(path, "wx", 384);
40109
+ const descriptor = openSync5(path, "wx", 384);
39194
40110
  let totalBytes = 0;
39195
40111
  try {
39196
40112
  for (const value of values) {
@@ -39203,7 +40119,7 @@ function writeJsonl(path, values, label) {
39203
40119
  throw new Error(`${label} exceeded ${PACK_JSONL_MAX_BYTES} bytes`);
39204
40120
  let offset = 0;
39205
40121
  while (offset < line.byteLength) {
39206
- const written = writeSync(descriptor, line, offset, line.byteLength - offset);
40122
+ const written = writeSync2(descriptor, line, offset, line.byteLength - offset);
39207
40123
  if (written <= 0)
39208
40124
  throw new Error(`${label} write made no progress`);
39209
40125
  offset += written;
@@ -39211,7 +40127,7 @@ function writeJsonl(path, values, label) {
39211
40127
  }
39212
40128
  fsyncSync2(descriptor);
39213
40129
  } finally {
39214
- closeSync4(descriptor);
40130
+ closeSync5(descriptor);
39215
40131
  }
39216
40132
  }
39217
40133
  function validPackMeta(value) {
@@ -39222,21 +40138,21 @@ function assertPackPathPart(value, label) {
39222
40138
  throw new Error(`${label} is invalid`);
39223
40139
  }
39224
40140
  function realDirectoryExists(path, label) {
39225
- if (!existsSync17(path))
40141
+ if (!existsSync19(path))
39226
40142
  return false;
39227
40143
  assertRealDirectory(path, label);
39228
40144
  return true;
39229
40145
  }
39230
40146
  function assertRealDirectory(path, label) {
39231
- const stat = lstatSync4(path);
40147
+ const stat = lstatSync5(path);
39232
40148
  if (stat.isSymbolicLink() || !stat.isDirectory())
39233
40149
  throw new Error(`${label} must be a real directory`);
39234
40150
  }
39235
40151
  function recordId(pack, pin, discriminator) {
39236
- return createHash10("sha256").update(`${pack}\x00${pin}\x00${discriminator}`).digest("hex").slice(0, 16);
40152
+ return createHash11("sha256").update(`${pack}\x00${pin}\x00${discriminator}`).digest("hex").slice(0, 16);
39237
40153
  }
39238
40154
  function sourceHash(body) {
39239
- return `sha256:${createHash10("sha256").update(body).digest("hex")}`;
40155
+ return `sha256:${createHash11("sha256").update(body).digest("hex")}`;
39240
40156
  }
39241
40157
  function extractEntities(recordId2, text2) {
39242
40158
  const found = new Set;
@@ -39265,7 +40181,7 @@ var init_pack = __esm(() => {
39265
40181
  init_atomic_file();
39266
40182
  init_private_path();
39267
40183
  init_file_read();
39268
- init_paths2();
40184
+ init_paths3();
39269
40185
  PACK_META_MAX_BYTES = 1024 * 1024;
39270
40186
  PACK_JSONL_MAX_BYTES = 256 * 1024 * 1024;
39271
40187
  PACK_LINE_MAX_BYTES = 64 * 1024 * 1024;
@@ -39289,34 +40205,34 @@ var init_pack = __esm(() => {
39289
40205
  });
39290
40206
 
39291
40207
  // src/agent-knowledge/ingest/taxonomy-pack.ts
39292
- import { closeSync as closeSync5, existsSync as existsSync18, fsyncSync as fsyncSync3, lstatSync as lstatSync5, mkdirSync as mkdirSync8, openSync as openSync5, readdirSync as readdirSync7, renameSync as renameSync4, rmSync as rmSync4, writeSync as writeSync2 } from "fs";
39293
- import { randomUUID as randomUUID5 } from "crypto";
39294
- import { join as join23 } from "path";
40208
+ import { closeSync as closeSync6, existsSync as existsSync20, fsyncSync as fsyncSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync9, openSync as openSync6, readdirSync as readdirSync8, renameSync as renameSync5, rmSync as rmSync5, writeSync as writeSync3 } from "fs";
40209
+ import { randomUUID as randomUUID6 } from "crypto";
40210
+ import { join as join25 } from "path";
39295
40211
  function writeTaxonomy(meta, nodes, edges) {
39296
40212
  assertPathPart(meta.id, "taxonomy id");
39297
40213
  assertPathPart(meta.pin, "taxonomy pin");
39298
40214
  const root = taxonomyDir();
39299
40215
  ensurePrivateDirectory(root, "knowledge taxonomy directory");
39300
- const dir = join23(root, `${meta.id}@${meta.pin}`);
39301
- const temporary = `${dir}.tmp-${process.pid}-${randomUUID5()}`;
39302
- mkdirSync8(temporary, {
40216
+ const dir = join25(root, `${meta.id}@${meta.pin}`);
40217
+ const temporary = `${dir}.tmp-${process.pid}-${randomUUID6()}`;
40218
+ mkdirSync9(temporary, {
39303
40219
  recursive: true,
39304
40220
  mode: 448
39305
40221
  });
39306
40222
  try {
39307
- atomicWriteFile(join23(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
40223
+ atomicWriteFile(join25(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
39308
40224
  `, 384);
39309
- writeJsonl2(join23(temporary, "nodes.jsonl"), nodes, "taxonomy nodes");
39310
- writeJsonl2(join23(temporary, "edges.jsonl"), edges, "taxonomy edges");
40225
+ writeJsonl2(join25(temporary, "nodes.jsonl"), nodes, "taxonomy nodes");
40226
+ writeJsonl2(join25(temporary, "edges.jsonl"), edges, "taxonomy edges");
39311
40227
  if (realDirectoryExists2(dir, "knowledge taxonomy"))
39312
- rmSync4(dir, {
40228
+ rmSync5(dir, {
39313
40229
  recursive: true,
39314
40230
  force: true
39315
40231
  });
39316
- renameSync4(temporary, dir);
40232
+ renameSync5(temporary, dir);
39317
40233
  syncDirectory(root);
39318
40234
  } catch (error) {
39319
- rmSync4(temporary, {
40235
+ rmSync5(temporary, {
39320
40236
  recursive: true,
39321
40237
  force: true
39322
40238
  });
@@ -39325,7 +40241,7 @@ function writeTaxonomy(meta, nodes, edges) {
39325
40241
  return dir;
39326
40242
  }
39327
40243
  function writeJsonl2(path, values, label) {
39328
- const descriptor = openSync5(path, "wx", 384);
40244
+ const descriptor = openSync6(path, "wx", 384);
39329
40245
  let totalBytes = 0;
39330
40246
  try {
39331
40247
  for (const value of values) {
@@ -39338,7 +40254,7 @@ function writeJsonl2(path, values, label) {
39338
40254
  throw new Error(`${label} exceeded ${TAXONOMY_JSONL_MAX_BYTES} bytes`);
39339
40255
  let offset = 0;
39340
40256
  while (offset < line.byteLength) {
39341
- const written = writeSync2(descriptor, line, offset, line.byteLength - offset);
40257
+ const written = writeSync3(descriptor, line, offset, line.byteLength - offset);
39342
40258
  if (written <= 0)
39343
40259
  throw new Error(`${label} write made no progress`);
39344
40260
  offset += written;
@@ -39346,24 +40262,24 @@ function writeJsonl2(path, values, label) {
39346
40262
  }
39347
40263
  fsyncSync3(descriptor);
39348
40264
  } finally {
39349
- closeSync5(descriptor);
40265
+ closeSync6(descriptor);
39350
40266
  }
39351
40267
  }
39352
40268
  function listTaxonomies() {
39353
40269
  const root = taxonomyDir();
39354
- if (!existsSync18(root))
40270
+ if (!existsSync20(root))
39355
40271
  return [];
39356
40272
  assertRealDirectory2(root, "knowledge taxonomy directory");
39357
- const entries = readdirSync7(root);
40273
+ const entries = readdirSync8(root);
39358
40274
  if (entries.length > TAXONOMY_DIRECTORY_MAX_COUNT)
39359
40275
  throw new Error(`knowledge taxonomy directory exceeded ${TAXONOMY_DIRECTORY_MAX_COUNT} entries`);
39360
40276
  const out = [];
39361
40277
  for (const entry of entries) {
39362
- const dir = join23(root, entry);
40278
+ const dir = join25(root, entry);
39363
40279
  try {
39364
40280
  assertRealDirectory2(dir, "knowledge taxonomy");
39365
- const metaPath = join23(dir, "meta.json");
39366
- if (!existsSync18(metaPath))
40281
+ const metaPath = join25(dir, "meta.json");
40282
+ if (!existsSync20(metaPath))
39367
40283
  continue;
39368
40284
  const meta = JSON.parse(readBoundedFileTextSyncNoFollow(metaPath, TAXONOMY_META_MAX_BYTES, "taxonomy metadata"));
39369
40285
  if (typeof meta?.id !== "string" || typeof meta.pin !== "string" || typeof meta.retrievedAt !== "string")
@@ -39387,14 +40303,14 @@ function latestTaxonomies() {
39387
40303
  }
39388
40304
  function readNodes(dir) {
39389
40305
  assertRealDirectory2(dir, "knowledge taxonomy");
39390
- return readJsonl2(join23(dir, "nodes.jsonl"));
40306
+ return readJsonl2(join25(dir, "nodes.jsonl"));
39391
40307
  }
39392
40308
  function readEdges(dir) {
39393
40309
  assertRealDirectory2(dir, "knowledge taxonomy");
39394
- return readJsonl2(join23(dir, "edges.jsonl"));
40310
+ return readJsonl2(join25(dir, "edges.jsonl"));
39395
40311
  }
39396
40312
  function readJsonl2(path) {
39397
- if (!existsSync18(path))
40313
+ if (!existsSync20(path))
39398
40314
  return [];
39399
40315
  const out = [];
39400
40316
  forEachFileLineSync(path, {
@@ -39419,19 +40335,19 @@ function assertPathPart(value, label) {
39419
40335
  throw new Error(`${label} is invalid`);
39420
40336
  }
39421
40337
  function realDirectoryExists2(path, label) {
39422
- if (!existsSync18(path))
40338
+ if (!existsSync20(path))
39423
40339
  return false;
39424
40340
  assertRealDirectory2(path, label);
39425
40341
  return true;
39426
40342
  }
39427
40343
  function assertRealDirectory2(path, label) {
39428
- const stat = lstatSync5(path);
40344
+ const stat = lstatSync6(path);
39429
40345
  if (stat.isSymbolicLink() || !stat.isDirectory())
39430
40346
  throw new Error(`${label} must be a real directory`);
39431
40347
  }
39432
40348
  var TAXONOMY_META_MAX_BYTES, TAXONOMY_JSONL_MAX_BYTES, TAXONOMY_LINE_MAX_BYTES, TAXONOMY_ENTRY_MAX_COUNT = 1e6, TAXONOMY_DIRECTORY_MAX_COUNT = 4096;
39433
40349
  var init_taxonomy_pack = __esm(() => {
39434
- init_paths2();
40350
+ init_paths3();
39435
40351
  init_file_read();
39436
40352
  init_atomic_file();
39437
40353
  init_private_path();
@@ -39474,16 +40390,16 @@ var init_http2 = __esm(() => {
39474
40390
  });
39475
40391
 
39476
40392
  // src/agent-knowledge/ingest/enrichment.ts
39477
- import { createReadStream, existsSync as existsSync19 } from "fs";
40393
+ import { createReadStream, existsSync as existsSync21 } from "fs";
39478
40394
  import { open as open3, rename, unlink as unlink2 } from "fs/promises";
39479
- import { randomUUID as randomUUID6 } from "crypto";
40395
+ import { randomUUID as randomUUID7 } from "crypto";
39480
40396
  import { Writable } from "stream";
39481
40397
  import { pipeline } from "stream/promises";
39482
40398
  import { StringDecoder as StringDecoder3 } from "string_decoder";
39483
40399
  import { createGunzip } from "zlib";
39484
- import { join as join24 } from "path";
40400
+ import { join as join26 } from "path";
39485
40401
  function enrichmentDir() {
39486
- return join24(knowledgeRoot(), "enrichment");
40402
+ return join26(knowledgeRoot(), "enrichment");
39487
40403
  }
39488
40404
  async function ingestEnrichment() {
39489
40405
  const map = new Map;
@@ -39547,8 +40463,8 @@ async function ingestEnrichment() {
39547
40463
  };
39548
40464
  }
39549
40465
  function readEnrichment() {
39550
- const path = join24(enrichmentDir(), "enrichment.jsonl");
39551
- if (!existsSync19(path))
40466
+ const path = join26(enrichmentDir(), "enrichment.jsonl");
40467
+ if (!existsSync21(path))
39552
40468
  return [];
39553
40469
  const out = [];
39554
40470
  forEachFileLineSync(path, {
@@ -39569,7 +40485,7 @@ function readEnrichment() {
39569
40485
  return out;
39570
40486
  }
39571
40487
  async function forEachEpssLine(dir, consume) {
39572
- const archive = join24(dir, `.epss-${randomUUID6()}.csv.gz`);
40488
+ const archive = join26(dir, `.epss-${randomUUID7()}.csv.gz`);
39573
40489
  try {
39574
40490
  await downloadKnowledgeFile(EPSS_URL, archive, EPSS_GZIP_MAX_BYTES, "epss archive");
39575
40491
  await pipeline(createReadStream(archive), createGunzip(), new BoundedLineSink(EPSS_CSV_MAX_BYTES, EPSS_LINE_MAX_BYTES, consume));
@@ -39580,8 +40496,8 @@ async function forEachEpssLine(dir, consume) {
39580
40496
  }
39581
40497
  }
39582
40498
  async function writeEnrichmentRows(dir, rows) {
39583
- const output = join24(dir, "enrichment.jsonl");
39584
- const temporary = join24(dir, `.enrichment-${randomUUID6()}.jsonl`);
40499
+ const output = join26(dir, "enrichment.jsonl");
40500
+ const temporary = join26(dir, `.enrichment-${randomUUID7()}.jsonl`);
39585
40501
  let file;
39586
40502
  try {
39587
40503
  file = await open3(temporary, "wx", 384);
@@ -39642,7 +40558,7 @@ function recordValue2(value) {
39642
40558
  }
39643
40559
  var KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json", EPSS_URL = "https://epss.empiricalsecurity.com/epss_scores-current.csv.gz", KEV_MAX_BYTES, EPSS_GZIP_MAX_BYTES, EPSS_CSV_MAX_BYTES, EPSS_LINE_MAX_BYTES, OUTPUT_CHUNK_BYTES, ENRICHMENT_FILE_MAX_BYTES, KEV_ENTRY_MAX_COUNT = 1e5, ENRICHMENT_ROW_MAX_COUNT = 1e6, BoundedLineSink;
39644
40560
  var init_enrichment = __esm(() => {
39645
- init_paths2();
40561
+ init_paths3();
39646
40562
  init_http2();
39647
40563
  init_file_read();
39648
40564
  init_private_path();
@@ -39699,18 +40615,18 @@ var init_enrichment = __esm(() => {
39699
40615
  });
39700
40616
 
39701
40617
  // src/agent-knowledge/build.ts
39702
- import { createHash as createHash11, randomUUID as randomUUID7 } from "crypto";
39703
- import { mkdirSync as mkdirSync9, renameSync as renameSync5, rmSync as rmSync5 } from "fs";
39704
- import { dirname as dirname9 } from "path";
40618
+ import { createHash as createHash12, randomUUID as randomUUID8 } from "crypto";
40619
+ import { mkdirSync as mkdirSync10, renameSync as renameSync6, rmSync as rmSync6 } from "fs";
40620
+ import { dirname as dirname10 } from "path";
39705
40621
  function buildKnowledgeDb(options = {}) {
39706
- const path = options.path ?? knowledgeDbPath();
39707
- const temporary = `${path}.tmp-${process.pid}-${randomUUID7()}`;
39708
- mkdirSync9(dirname9(path), {
40622
+ const path = options.path ?? legacyKnowledgeDbPath();
40623
+ const temporary = `${path}.tmp-${process.pid}-${randomUUID8()}`;
40624
+ mkdirSync10(dirname10(path), {
39709
40625
  recursive: true,
39710
40626
  mode: 448
39711
40627
  });
39712
40628
  if (options.path === undefined)
39713
- ensurePrivateDirectory(dirname9(path), "farai home directory");
40629
+ ensurePrivateDirectory(dirname10(path), "farai home directory");
39714
40630
  ensurePrivateRegularFileIfExists(path, "knowledge database");
39715
40631
  const store = new KnowledgeStore(temporary, true);
39716
40632
  const db = store.writable();
@@ -39767,18 +40683,18 @@ function buildKnowledgeDb(options = {}) {
39767
40683
  const actualEdges = rowCount(db, "kb_edges");
39768
40684
  store.close();
39769
40685
  ensurePrivateSqlitePath(temporary, "staged knowledge database");
39770
- rmSync5(`${temporary}-wal`, {
40686
+ rmSync6(`${temporary}-wal`, {
39771
40687
  force: true
39772
40688
  });
39773
- rmSync5(`${temporary}-shm`, {
40689
+ rmSync6(`${temporary}-shm`, {
39774
40690
  force: true
39775
40691
  });
39776
- rmSync5(`${temporary}-journal`, {
40692
+ rmSync6(`${temporary}-journal`, {
39777
40693
  force: true
39778
40694
  });
39779
- renameSync5(temporary, path);
40695
+ renameSync6(temporary, path);
39780
40696
  ensurePrivateRegularFileIfExists(path, "knowledge database");
39781
- syncDirectory(dirname9(path));
40697
+ syncDirectory(dirname10(path));
39782
40698
  return {
39783
40699
  path,
39784
40700
  packs: packs.length,
@@ -39793,16 +40709,16 @@ function buildKnowledgeDb(options = {}) {
39793
40709
  try {
39794
40710
  store.close();
39795
40711
  } catch {}
39796
- rmSync5(temporary, {
40712
+ rmSync6(temporary, {
39797
40713
  force: true
39798
40714
  });
39799
- rmSync5(`${temporary}-wal`, {
40715
+ rmSync6(`${temporary}-wal`, {
39800
40716
  force: true
39801
40717
  });
39802
- rmSync5(`${temporary}-shm`, {
40718
+ rmSync6(`${temporary}-shm`, {
39803
40719
  force: true
39804
40720
  });
39805
- rmSync5(`${temporary}-journal`, {
40721
+ rmSync6(`${temporary}-journal`, {
39806
40722
  force: true
39807
40723
  });
39808
40724
  throw error;
@@ -39826,7 +40742,7 @@ function trackDuplicate(groups, record3) {
39826
40742
  const normalized = record3.answer.replace(/\s+/g, " ").trim().toLowerCase();
39827
40743
  if (normalized.length < 64)
39828
40744
  return;
39829
- const key = createHash11("sha256").update(normalized).digest("hex");
40745
+ const key = createHash12("sha256").update(normalized).digest("hex");
39830
40746
  const list = groups.get(key) ?? [];
39831
40747
  list.push(record3.id);
39832
40748
  groups.set(key, list);
@@ -39849,24 +40765,24 @@ var init_build = __esm(() => {
39849
40765
  init_atomic_file();
39850
40766
  init_private_path();
39851
40767
  init_store();
39852
- init_paths2();
40768
+ init_paths3();
39853
40769
  init_pack();
39854
40770
  init_taxonomy_pack();
39855
40771
  init_enrichment();
39856
40772
  });
39857
40773
 
39858
40774
  // src/agent-knowledge/ingest/git-source.ts
39859
- import { existsSync as existsSync20, lstatSync as lstatSync6, renameSync as renameSync6, rmSync as rmSync6 } from "fs";
39860
- import { randomUUID as randomUUID8 } from "crypto";
39861
- import { join as join25 } from "path";
40775
+ import { existsSync as existsSync22, lstatSync as lstatSync7, renameSync as renameSync7, rmSync as rmSync7 } from "fs";
40776
+ import { randomUUID as randomUUID9 } from "crypto";
40777
+ import { join as join27 } from "path";
39862
40778
  async function fetchGitSource(source) {
39863
40779
  assertSource(source);
39864
40780
  const root = cacheDir();
39865
40781
  ensurePrivateDirectory(root, "knowledge cache directory");
39866
- const dir = join25(root, source.id);
40782
+ const dir = join27(root, source.id);
39867
40783
  const cached = cachedRepositoryExists(dir);
39868
40784
  if (!cached) {
39869
- const staging = join25(root, `.${source.id}-clone-${randomUUID8()}`);
40785
+ const staging = join27(root, `.${source.id}-clone-${randomUUID9()}`);
39870
40786
  try {
39871
40787
  const args = ["clone", "--depth", "1", "--branch", source.branch];
39872
40788
  if (source.sparse?.length)
@@ -39875,9 +40791,9 @@ async function fetchGitSource(source) {
39875
40791
  await run("git", args);
39876
40792
  if (source.sparse?.length)
39877
40793
  await run("git", ["-C", staging, "sparse-checkout", "set", "--no-cone", "--", ...source.sparse]);
39878
- renameSync6(staging, dir);
40794
+ renameSync7(staging, dir);
39879
40795
  } finally {
39880
- rmSync6(staging, {
40796
+ rmSync7(staging, {
39881
40797
  recursive: true,
39882
40798
  force: true
39883
40799
  });
@@ -39945,22 +40861,22 @@ function assertSource(source) {
39945
40861
  throw new Error(`invalid sparse path for git source: ${source.id}`);
39946
40862
  }
39947
40863
  function cachedRepositoryExists(dir) {
39948
- if (!existsSync20(dir))
40864
+ if (!existsSync22(dir))
39949
40865
  return false;
39950
- const directory = lstatSync6(dir);
40866
+ const directory = lstatSync7(dir);
39951
40867
  if (directory.isSymbolicLink() || !directory.isDirectory())
39952
40868
  throw new Error(`git source cache must be a real directory: ${dir}`);
39953
- const git = join25(dir, ".git");
39954
- if (!existsSync20(git))
40869
+ const git = join27(dir, ".git");
40870
+ if (!existsSync22(git))
39955
40871
  throw new Error(`git source cache is not a repository: ${dir}`);
39956
- const metadata = lstatSync6(git);
40872
+ const metadata = lstatSync7(git);
39957
40873
  if (metadata.isSymbolicLink() || !metadata.isDirectory())
39958
40874
  throw new Error(`git source metadata must be a real directory: ${dir}`);
39959
40875
  return true;
39960
40876
  }
39961
40877
  var GIT_TIMEOUT_MS;
39962
40878
  var init_git_source = __esm(() => {
39963
- init_paths2();
40879
+ init_paths3();
39964
40880
  init_captured_process();
39965
40881
  init_output_buffer();
39966
40882
  init_private_path();
@@ -40072,11 +40988,11 @@ var init_markdown_chunk = __esm(() => {
40072
40988
  });
40073
40989
 
40074
40990
  // src/agent-knowledge/ingest/hacktricks.ts
40075
- import { existsSync as existsSync21, lstatSync as lstatSync7, readdirSync as readdirSync8 } from "fs";
40076
- import { join as join26, relative as relative8 } from "path";
40991
+ import { existsSync as existsSync23, lstatSync as lstatSync8, readdirSync as readdirSync9 } from "fs";
40992
+ import { join as join28, relative as relative8 } from "path";
40077
40993
  async function ingestHacktricks() {
40078
40994
  const fetched = await fetchGitSource(SOURCE);
40079
- const srcDir = join26(fetched.dir, "src");
40995
+ const srcDir = join28(fetched.dir, "src");
40080
40996
  const meta = {
40081
40997
  id: "hacktricks",
40082
40998
  sourceUrl: "https://github.com/HackTricks-wiki/hacktricks",
@@ -40128,9 +41044,9 @@ async function ingestHacktricks() {
40128
41044
  };
40129
41045
  }
40130
41046
  function markdownFiles(root) {
40131
- if (!existsSync21(root))
41047
+ if (!existsSync23(root))
40132
41048
  return [];
40133
- const rootInfo = lstatSync7(root);
41049
+ const rootInfo = lstatSync8(root);
40134
41050
  if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
40135
41051
  throw new Error("hacktricks source root must be a real directory");
40136
41052
  const out = [];
@@ -40138,12 +41054,12 @@ function markdownFiles(root) {
40138
41054
  let entries = 0;
40139
41055
  while (pending.length) {
40140
41056
  const dir = pending.pop();
40141
- for (const entry of readdirSync8(dir)) {
41057
+ for (const entry of readdirSync9(dir)) {
40142
41058
  entries += 1;
40143
41059
  if (entries > WALK_ENTRY_MAX_COUNT)
40144
41060
  throw new Error(`hacktricks source exceeded ${WALK_ENTRY_MAX_COUNT} entries`);
40145
- const full = join26(dir, entry);
40146
- const info = lstatSync7(full);
41061
+ const full = join28(dir, entry);
41062
+ const info = lstatSync8(full);
40147
41063
  if (info.isSymbolicLink())
40148
41064
  continue;
40149
41065
  if (info.isDirectory())
@@ -40173,8 +41089,8 @@ var init_hacktricks = __esm(() => {
40173
41089
  });
40174
41090
 
40175
41091
  // src/agent-knowledge/ingest/payloads.ts
40176
- import { existsSync as existsSync22, lstatSync as lstatSync8, readdirSync as readdirSync9 } from "fs";
40177
- import { join as join27, relative as relative9 } from "path";
41092
+ import { existsSync as existsSync24, lstatSync as lstatSync9, readdirSync as readdirSync10 } from "fs";
41093
+ import { join as join29, relative as relative9 } from "path";
40178
41094
  async function ingestPayloads() {
40179
41095
  const fetched = await fetchGitSource(SOURCE2);
40180
41096
  const meta = {
@@ -40230,9 +41146,9 @@ async function ingestPayloads() {
40230
41146
  };
40231
41147
  }
40232
41148
  function markdownFiles2(root) {
40233
- if (!existsSync22(root))
41149
+ if (!existsSync24(root))
40234
41150
  return [];
40235
- const rootInfo = lstatSync8(root);
41151
+ const rootInfo = lstatSync9(root);
40236
41152
  if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
40237
41153
  throw new Error("payload source root must be a real directory");
40238
41154
  const out = [];
@@ -40240,14 +41156,14 @@ function markdownFiles2(root) {
40240
41156
  let entries = 0;
40241
41157
  while (pending.length) {
40242
41158
  const dir = pending.pop();
40243
- for (const entry of readdirSync9(dir)) {
41159
+ for (const entry of readdirSync10(dir)) {
40244
41160
  if (entry === ".git")
40245
41161
  continue;
40246
41162
  entries += 1;
40247
41163
  if (entries > WALK_ENTRY_MAX_COUNT2)
40248
41164
  throw new Error(`payload source exceeded ${WALK_ENTRY_MAX_COUNT2} entries`);
40249
- const full = join27(dir, entry);
40250
- const info = lstatSync8(full);
41165
+ const full = join29(dir, entry);
41166
+ const info = lstatSync9(full);
40251
41167
  if (info.isSymbolicLink())
40252
41168
  continue;
40253
41169
  if (info.isDirectory())
@@ -40489,15 +41405,15 @@ var init_attack = __esm(() => {
40489
41405
  });
40490
41406
 
40491
41407
  // src/agent-knowledge/ingest/zip-fetch.ts
40492
- import { existsSync as existsSync23, lstatSync as lstatSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, renameSync as renameSync7, rmSync as rmSync7, unlinkSync as unlinkSync6 } from "fs";
40493
- import { randomUUID as randomUUID9 } from "crypto";
40494
- import { basename as basename5, join as join28, relative as relative10, resolve as resolve9 } from "path";
41408
+ import { existsSync as existsSync25, lstatSync as lstatSync10, mkdirSync as mkdirSync11, readdirSync as readdirSync11, renameSync as renameSync8, rmSync as rmSync8, unlinkSync as unlinkSync7 } from "fs";
41409
+ import { randomUUID as randomUUID10 } from "crypto";
41410
+ import { basename as basename5, join as join30, relative as relative10, resolve as resolve10 } from "path";
40495
41411
  async function fetchZippedXml(id2, url, pattern) {
40496
- const dir = join28(cacheDir(), id2);
41412
+ const dir = join30(cacheDir(), id2);
40497
41413
  ensurePrivateDirectory(dir, `${id2} knowledge cache directory`);
40498
- const token = randomUUID9();
40499
- const zipPath = join28(dir, `.download-${token}.zip`);
40500
- const staging = join28(dir, `.extract-${token}`);
41414
+ const token = randomUUID10();
41415
+ const zipPath = join30(dir, `.download-${token}.zip`);
41416
+ const staging = join30(dir, `.extract-${token}`);
40501
41417
  try {
40502
41418
  await downloadKnowledgeFile(url, zipPath, ZIP_MAX_BYTES, `${id2} zip archive`);
40503
41419
  const entries = await listArchiveEntries(zipPath);
@@ -40507,7 +41423,7 @@ async function fetchZippedXml(id2, url, pattern) {
40507
41423
  const declaredSize = await archiveEntrySize(zipPath, selected);
40508
41424
  if (declaredSize > XML_MAX_BYTES)
40509
41425
  throw new Error(`${id2} xml exceeded the ${XML_MAX_BYTES}-byte expanded limit`);
40510
- mkdirSync10(staging, {
41426
+ mkdirSync11(staging, {
40511
41427
  recursive: true
40512
41428
  });
40513
41429
  const extracted = await runCapturedProcess("unzip", ["-o", "-q", zipPath, selected, "-d", staging], {
@@ -40518,26 +41434,26 @@ async function fetchZippedXml(id2, url, pattern) {
40518
41434
  throw new Error(`${id2} archive extraction timed out`);
40519
41435
  if (extracted.exitCode !== 0)
40520
41436
  throw new Error(extracted.stderr.trim() || `${id2} archive extraction failed`);
40521
- const stagedPath = resolve9(staging, selected);
41437
+ const stagedPath = resolve10(staging, selected);
40522
41438
  const stagedRelative = relative10(staging, stagedPath);
40523
41439
  if (stagedRelative.startsWith("..") || stagedRelative.startsWith("/"))
40524
41440
  throw new Error(`${id2} archive entry escaped the extraction directory`);
40525
- const info = lstatSync9(stagedPath);
41441
+ const info = lstatSync10(stagedPath);
40526
41442
  if (!info.isFile() || info.isSymbolicLink())
40527
41443
  throw new Error(`${id2} archive entry was not a regular file`);
40528
41444
  if (info.size > XML_MAX_BYTES || info.size !== declaredSize)
40529
41445
  throw new Error(`${id2} archive entry size did not match its validated metadata`);
40530
- const path = join28(dir, `${id2}.xml`);
40531
- renameSync7(stagedPath, path);
41446
+ const path = join30(dir, `${id2}.xml`);
41447
+ renameSync8(stagedPath, path);
40532
41448
  return {
40533
41449
  xml: readBoundedXml(path),
40534
41450
  path
40535
41451
  };
40536
41452
  } finally {
40537
41453
  try {
40538
- unlinkSync6(zipPath);
41454
+ unlinkSync7(zipPath);
40539
41455
  } catch {}
40540
- rmSync7(staging, {
41456
+ rmSync8(staging, {
40541
41457
  recursive: true,
40542
41458
  force: true
40543
41459
  });
@@ -40595,7 +41511,7 @@ function readBoundedXml(path) {
40595
41511
  }
40596
41512
  var ZIP_MAX_BYTES, XML_MAX_BYTES, ZIP_LIST_MAX_BYTES, ZIP_ENTRY_MAX_COUNT = 4096, UNZIP_TIMEOUT_MS = 60000;
40597
41513
  var init_zip_fetch = __esm(() => {
40598
- init_paths2();
41514
+ init_paths3();
40599
41515
  init_http2();
40600
41516
  init_captured_process();
40601
41517
  init_file_read();
@@ -40898,7 +41814,7 @@ var init_command = __esm(() => {
40898
41814
  init_pack();
40899
41815
  init_taxonomy_pack();
40900
41816
  init_store();
40901
- init_paths2();
41817
+ init_paths3();
40902
41818
  init_hacktricks();
40903
41819
  init_payloads();
40904
41820
  init_attack();
@@ -40918,6 +41834,80 @@ var init_command = __esm(() => {
40918
41834
  CORPUS_INGEST = new Set(["hacktricks", "payloads", ...TAXONOMY_INGEST]);
40919
41835
  });
40920
41836
 
41837
+ // src/agent-content/preflight.ts
41838
+ var exports_preflight = {};
41839
+ __export(exports_preflight, {
41840
+ runStartupContentPreflight: () => runStartupContentPreflight
41841
+ });
41842
+ import { createInterface } from "readline";
41843
+ async function runStartupContentPreflight(workspace) {
41844
+ const status2 = await checkContentUpdate({
41845
+ workspace
41846
+ });
41847
+ if (status2.state !== "update_available" || !status2.manifest)
41848
+ return "continue";
41849
+ if (isContentVersionDismissed(status2.manifest.contentVersion))
41850
+ return "continue";
41851
+ const config = loadConfig(workspace);
41852
+ if (config.updates?.prompt === false || !process.stdin.isTTY || !process.stdout.isTTY)
41853
+ return "continue";
41854
+ const answer = await promptForUpdate(status2.manifest.contentVersion, Boolean(status2.manifest.knowledge), Boolean(status2.manifest.skills));
41855
+ if (answer === "cancelled")
41856
+ return "cancelled";
41857
+ if (answer === "dismiss") {
41858
+ dismissContentVersion(status2.manifest.contentVersion);
41859
+ return "continue";
41860
+ }
41861
+ if (answer === "later")
41862
+ return "continue";
41863
+ console.log("updating farai content...");
41864
+ try {
41865
+ const applied = await applyContentUpdate(status2.manifest, status2.manifestUrl);
41866
+ const parts = [applied.knowledge ? "knowledge" : undefined, applied.skills ? "skills" : undefined].filter(Boolean).join(" + ");
41867
+ console.log(`updated farai content to ${applied.version}${parts ? ` (${parts})` : ""}`);
41868
+ } catch (error) {
41869
+ console.error(`content update failed: ${errorMessage7(error)}`);
41870
+ console.error("starting farai with the current content");
41871
+ }
41872
+ return "continue";
41873
+ }
41874
+ async function promptForUpdate(version, knowledge, skills) {
41875
+ const contents = [knowledge ? "knowledge" : undefined, skills ? "skills" : undefined].filter(Boolean).join(" + ");
41876
+ console.log("");
41877
+ console.log(`farai content ${version} is available${contents ? ` (${contents})` : ""}`);
41878
+ const interfaceHandle = createInterface({
41879
+ input: process.stdin,
41880
+ output: process.stdout
41881
+ });
41882
+ return await new Promise((resolve11) => {
41883
+ let settled = false;
41884
+ const finish = (value) => {
41885
+ if (settled)
41886
+ return;
41887
+ settled = true;
41888
+ interfaceHandle.close();
41889
+ resolve11(value);
41890
+ };
41891
+ interfaceHandle.once("SIGINT", () => finish("cancelled"));
41892
+ interfaceHandle.question("update before starting? [enter=yes, n=later, d=skip version] ", (value) => {
41893
+ const normalized = value.trim().toLowerCase();
41894
+ if (normalized === "d" || normalized === "dismiss")
41895
+ finish("dismiss");
41896
+ else if (normalized === "n" || normalized === "no")
41897
+ finish("later");
41898
+ else
41899
+ finish("apply");
41900
+ });
41901
+ });
41902
+ }
41903
+ function errorMessage7(error) {
41904
+ return error instanceof Error ? error.message : String(error);
41905
+ }
41906
+ var init_preflight = __esm(() => {
41907
+ init_config();
41908
+ init_updater();
41909
+ });
41910
+
40921
41911
  // node_modules/solid-js/dist/solid.js
40922
41912
  function getContextId(count2) {
40923
41913
  const num2 = String(count2), len = num2.length - 1;
@@ -49538,10 +50528,10 @@ function artifactRow(part, width) {
49538
50528
  return row;
49539
50529
  }
49540
50530
  const note = extractObject(payload, "note");
49541
- const artifact = extractObject(payload, "artifact") ?? extractObject(payload, "outputArtifact");
49542
- const title = artifactTitle(kind, note, artifact);
49543
- const detail = firstSemanticLine(stringField2(note, "text")) ?? stringField2(artifact, "path") ?? extractField(payload, "path") ?? extractField(payload, "title") ?? humanLabel(kind);
49544
- const body = artifactBody(payload, note, artifact);
50531
+ const artifact2 = extractObject(payload, "artifact") ?? extractObject(payload, "outputArtifact");
50532
+ const title = artifactTitle(kind, note, artifact2);
50533
+ const detail = firstSemanticLine(stringField2(note, "text")) ?? stringField2(artifact2, "path") ?? extractField(payload, "path") ?? extractField(payload, "title") ?? humanLabel(kind);
50534
+ const body = artifactBody(payload, note, artifact2);
49545
50535
  return {
49546
50536
  kind: "artifact",
49547
50537
  title,
@@ -49552,8 +50542,8 @@ function artifactRow(part, width) {
49552
50542
  id: part.id
49553
50543
  };
49554
50544
  }
49555
- function artifactBody(payload, note, artifact) {
49556
- const candidates = [stringField2(note, "text"), stringField2(artifact, "content"), stringField2(artifact, "body"), stringField2(artifact, "text"), stringField2(artifact, "summary"), stringField2(artifact, "output"), extractField(payload, "content"), extractField(payload, "body"), extractField(payload, "text"), extractField(payload, "summary"), extractField(payload, "output")];
50545
+ function artifactBody(payload, note, artifact2) {
50546
+ const candidates = [stringField2(note, "text"), stringField2(artifact2, "content"), stringField2(artifact2, "body"), stringField2(artifact2, "text"), stringField2(artifact2, "summary"), stringField2(artifact2, "output"), extractField(payload, "content"), extractField(payload, "body"), extractField(payload, "text"), extractField(payload, "summary"), extractField(payload, "output")];
49557
50547
  return candidates.find((value) => Boolean(value?.trim()))?.trim();
49558
50548
  }
49559
50549
  function firstSemanticLine(value) {
@@ -49568,10 +50558,10 @@ function mcpInventoryRow(part, width) {
49568
50558
  id: part.id
49569
50559
  };
49570
50560
  }
49571
- function artifactTitle(kind, note, artifact) {
50561
+ function artifactTitle(kind, note, artifact2) {
49572
50562
  if (stringField2(note, "text"))
49573
50563
  return "saved note";
49574
- if (stringField2(artifact, "path"))
50564
+ if (stringField2(artifact2, "path"))
49575
50565
  return "saved artifact";
49576
50566
  return humanLabel(kind);
49577
50567
  }
@@ -50147,7 +51137,7 @@ function createStoreResourceController(input) {
50147
51137
  actions.servicesSet(services.value);
50148
51138
  if (catalog.status === "fulfilled")
50149
51139
  actions.mcpCatalogSet(catalog.value.servers, catalog.value.statuses);
50150
- const errors = [refreshError, services.status === "rejected" ? errorMessage6(services.reason) : undefined, catalog.status === "rejected" ? errorMessage6(catalog.reason) : undefined].filter((message) => Boolean(message));
51140
+ const errors = [refreshError, services.status === "rejected" ? errorMessage8(services.reason) : undefined, catalog.status === "rejected" ? errorMessage8(catalog.reason) : undefined].filter((message) => Boolean(message));
50151
51141
  actions.mcpStatusErrorSet(errors.length ? [...new Set(errors)].join(" \xB7 ") : undefined);
50152
51142
  } finally {
50153
51143
  if (mcpOverlayGeneration === generation && sessions2.owns(owner) && store.ui.statusDetail === "refreshing mcp") {
@@ -50169,7 +51159,7 @@ function createStoreResourceController(input) {
50169
51159
  actions.emailCatalogSet(catalog.accounts);
50170
51160
  } catch (error) {
50171
51161
  if (emailOverlayGeneration === generation && sessions2.owns(owner))
50172
- actions.errorSet(errorMessage6(error));
51162
+ actions.errorSet(errorMessage8(error));
50173
51163
  } finally {
50174
51164
  if (emailOverlayGeneration === generation && sessions2.owns(owner) && store.ui.statusDetail === "loading email") {
50175
51165
  input.setStatusDetail(undefined);
@@ -50215,7 +51205,7 @@ function createStoreResourceController(input) {
50215
51205
  if (!sessions2.owns(owner))
50216
51206
  return;
50217
51207
  if (options.reportError !== false) {
50218
- actions.errorSet(errorMessage6(error));
51208
+ actions.errorSet(errorMessage8(error));
50219
51209
  return;
50220
51210
  }
50221
51211
  throw error;
@@ -50258,7 +51248,7 @@ function containerState(imageExists, imageContractCurrent, persistentRunning, pe
50258
51248
  return "missing";
50259
51249
  return persistentRunning && persistentImageCurrent ? "running" : "stopped";
50260
51250
  }
50261
- function errorMessage6(error) {
51251
+ function errorMessage8(error) {
50262
51252
  return error instanceof Error ? error.message : String(error);
50263
51253
  }
50264
51254
 
@@ -50302,7 +51292,7 @@ function createStorePromptController(input) {
50302
51292
  await port.prompt(sessionId, text2);
50303
51293
  } catch (error) {
50304
51294
  if (isActiveSession(sessionId))
50305
- actions.errorSet(errorMessage7(error));
51295
+ actions.errorSet(errorMessage9(error));
50306
51296
  } finally {
50307
51297
  if (submissions.get(sessionId) !== submission)
50308
51298
  return;
@@ -50314,7 +51304,7 @@ function createStorePromptController(input) {
50314
51304
  await sessions2.requestSnapshotRefresh(sessionId);
50315
51305
  } catch (error) {
50316
51306
  if (isActiveSession(sessionId))
50317
- actions.errorSet(errorMessage7(error));
51307
+ actions.errorSet(errorMessage9(error));
50318
51308
  }
50319
51309
  }
50320
51310
  })();
@@ -50337,7 +51327,7 @@ function createStorePromptController(input) {
50337
51327
  return submitted;
50338
51328
  } catch (error) {
50339
51329
  if (sessions2.owns(owner))
50340
- actions.errorSet(errorMessage7(error));
51330
+ actions.errorSet(errorMessage9(error));
50341
51331
  return false;
50342
51332
  } finally {
50343
51333
  if (sessions2.owns(owner) && store.ui.statusDetail === status2)
@@ -50364,7 +51354,7 @@ function createStorePromptController(input) {
50364
51354
  } catch (error) {
50365
51355
  if (sessions2.owns(owner) && store.snapshot.pendingUserInput?.id === requestId) {
50366
51356
  actions.requestUserInputSubmittingSet(false);
50367
- actions.errorSet(errorMessage7(error));
51357
+ actions.errorSet(errorMessage9(error));
50368
51358
  }
50369
51359
  return false;
50370
51360
  }
@@ -50407,7 +51397,7 @@ function createStorePromptController(input) {
50407
51397
  await sessions2.requestSnapshotRefresh(owner.sessionId);
50408
51398
  } catch (error) {
50409
51399
  if (sessions2.owns(owner) && store.snapshot.pendingUserInput?.id === requestId)
50410
- actions.errorSet(errorMessage7(error));
51400
+ actions.errorSet(errorMessage9(error));
50411
51401
  }
50412
51402
  }
50413
51403
  function queuePrompt(text2) {
@@ -50425,7 +51415,7 @@ function createStorePromptController(input) {
50425
51415
  return true;
50426
51416
  } catch (error) {
50427
51417
  if (owner && sessions2.owns(owner))
50428
- actions.errorSet(errorMessage7(error));
51418
+ actions.errorSet(errorMessage9(error));
50429
51419
  return false;
50430
51420
  }
50431
51421
  }
@@ -50451,7 +51441,7 @@ function createStorePromptController(input) {
50451
51441
  } catch (error) {
50452
51442
  if (!sessions2.owns(owner))
50453
51443
  return;
50454
- const message = errorMessage7(error);
51444
+ const message = errorMessage9(error);
50455
51445
  if (!/abort|cancel/i.test(message))
50456
51446
  actions.errorSet(message);
50457
51447
  } finally {
@@ -50476,7 +51466,7 @@ function createStorePromptController(input) {
50476
51466
  input.setStatusDetail("conversation cleared", 1500);
50477
51467
  } catch (error) {
50478
51468
  if (sessions2.owns(owner))
50479
- actions.errorSet(errorMessage7(error));
51469
+ actions.errorSet(errorMessage9(error));
50480
51470
  }
50481
51471
  }
50482
51472
  async function cancelCurrentTurn() {
@@ -50499,7 +51489,7 @@ function createStorePromptController(input) {
50499
51489
  await port.cancelTurn(turnId, "cancelled by user");
50500
51490
  } catch (error) {
50501
51491
  if (sessions2.owns(owner))
50502
- actions.errorSet(errorMessage7(error));
51492
+ actions.errorSet(errorMessage9(error));
50503
51493
  }
50504
51494
  if (!sessions2.owns(owner))
50505
51495
  return;
@@ -50507,7 +51497,7 @@ function createStorePromptController(input) {
50507
51497
  await sessions2.requestSnapshotRefresh(owner.sessionId);
50508
51498
  } catch (error) {
50509
51499
  if (sessions2.owns(owner))
50510
- actions.errorSet(errorMessage7(error));
51500
+ actions.errorSet(errorMessage9(error));
50511
51501
  }
50512
51502
  }
50513
51503
  return {
@@ -50531,7 +51521,7 @@ function mergeQueuedPrompts(current, queued) {
50531
51521
  return current;
50532
51522
  return [...current, queued].sort((left, right) => left.sequence - right.sequence);
50533
51523
  }
50534
- function errorMessage7(error) {
51524
+ function errorMessage9(error) {
50535
51525
  return error instanceof Error ? error.message : String(error);
50536
51526
  }
50537
51527
  var init_store_prompt_controller = __esm(() => {
@@ -54218,9 +55208,9 @@ function parseCommandArguments(value) {
54218
55208
 
54219
55209
  // src/agent-tui/clipboard.ts
54220
55210
  import { spawnSync } from "child_process";
54221
- import { mkdtempSync, rmSync as rmSync8, writeFileSync as writeFileSync3 } from "fs";
55211
+ import { mkdtempSync, rmSync as rmSync9, writeFileSync as writeFileSync3 } from "fs";
54222
55212
  import { tmpdir as tmpdir2 } from "os";
54223
- import { join as join29 } from "path";
55213
+ import { join as join31 } from "path";
54224
55214
  function writeClipboard(text2) {
54225
55215
  if (!text2)
54226
55216
  return {
@@ -54272,8 +55262,8 @@ function runClipboardCommand(command, text2) {
54272
55262
  };
54273
55263
  }
54274
55264
  function writeClipboardWithAppleScript(text2) {
54275
- const dir = mkdtempSync(join29(tmpdir2(), "farai-clipboard-"));
54276
- const path = join29(dir, "clipboard.txt");
55265
+ const dir = mkdtempSync(join31(tmpdir2(), "farai-clipboard-"));
55266
+ const path = join31(dir, "clipboard.txt");
54277
55267
  try {
54278
55268
  writeFileSync3(path, text2, {
54279
55269
  encoding: "utf8",
@@ -54297,7 +55287,7 @@ function writeClipboardWithAppleScript(text2) {
54297
55287
  error: `osascript: ${detail}`
54298
55288
  };
54299
55289
  } finally {
54300
- rmSync8(dir, {
55290
+ rmSync9(dir, {
54301
55291
  recursive: true,
54302
55292
  force: true
54303
55293
  });
@@ -54318,9 +55308,9 @@ function ctrlCDecision(text2, armedUntil, now = Date.now()) {
54318
55308
  }
54319
55309
 
54320
55310
  // src/agent-tui/input/composer-controller.ts
54321
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync9, writeFileSync as writeFileSync4 } from "fs";
55311
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync10, writeFileSync as writeFileSync4 } from "fs";
54322
55312
  import { tmpdir as tmpdir3 } from "os";
54323
- import { join as join30 } from "path";
55313
+ import { join as join32 } from "path";
54324
55314
  function createComposerController(input) {
54325
55315
  const {
54326
55316
  tui,
@@ -54588,8 +55578,8 @@ function createComposerController(input) {
54588
55578
  });
54589
55579
  return;
54590
55580
  }
54591
- const dir = mkdtempSync2(join30(tmpdir3(), "farai-editor-"));
54592
- const file = join30(dir, "prompt.md");
55581
+ const dir = mkdtempSync2(join32(tmpdir3(), "farai-editor-"));
55582
+ const file = join32(dir, "prompt.md");
54593
55583
  writeFileSync4(file, composer.ref()?.plainText ?? composer.text(), {
54594
55584
  encoding: "utf8",
54595
55585
  mode: 384
@@ -54623,7 +55613,7 @@ function createComposerController(input) {
54623
55613
  renderer.resume();
54624
55614
  } catch {}
54625
55615
  try {
54626
- rmSync9(dir, {
55616
+ rmSync10(dir, {
54627
55617
  recursive: true,
54628
55618
  force: true
54629
55619
  });
@@ -57472,7 +58462,7 @@ function ToolResultContext(props) {
57472
58462
  const evidence = () => result()?.evidence ?? [];
57473
58463
  const attachments = () => result()?.attachments ?? [];
57474
58464
  const metadata = () => result()?.metadata ?? {};
57475
- const artifact = () => {
58465
+ const artifact2 = () => {
57476
58466
  const value = metadata().outputArtifact;
57477
58467
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
57478
58468
  };
@@ -57514,7 +58504,7 @@ function ToolResultContext(props) {
57514
58504
  }), null);
57515
58505
  insert(_el$31, createComponent2(Show, {
57516
58506
  get when() {
57517
- return result()?.outputArtifactId || artifact();
58507
+ return result()?.outputArtifactId || artifact2();
57518
58508
  },
57519
58509
  get children() {
57520
58510
  var _el$35 = createElement("box"), _el$36 = createElement("text");
@@ -57530,7 +58520,7 @@ function ToolResultContext(props) {
57530
58520
  ...result()?.outputArtifactId ? {
57531
58521
  id: result()?.outputArtifactId
57532
58522
  } : {},
57533
- ...artifact() ?? {}
58523
+ ...artifact2() ?? {}
57534
58524
  });
57535
58525
  },
57536
58526
  children: (line) => (() => {
@@ -65904,7 +66894,7 @@ var init_app = __esm(() => {
65904
66894
  });
65905
66895
 
65906
66896
  // src/agent-tui/update-check.ts
65907
- import { dirname as dirname10, join as join31 } from "path";
66897
+ import { dirname as dirname11, join as join33 } from "path";
65908
66898
  function prepareUpdateCheck(options = {}) {
65909
66899
  if (updateCheckDisabled())
65910
66900
  return {
@@ -65947,42 +66937,9 @@ function createUpdateNotice(currentVersion, latestVersion) {
65947
66937
  updateCommand: "npm install -g farai@latest"
65948
66938
  };
65949
66939
  }
65950
- function compareSemver(left, right) {
65951
- const a = parseSemver(left);
65952
- const b = parseSemver(right);
65953
- if (!a || !b)
65954
- return 0;
65955
- for (let index = 0;index < 3; index += 1) {
65956
- const delta = a.core[index] - b.core[index];
65957
- if (delta !== 0)
65958
- return delta < 0 ? -1 : 1;
65959
- }
65960
- if (a.prerelease.length === 0 || b.prerelease.length === 0) {
65961
- if (a.prerelease.length === b.prerelease.length)
65962
- return 0;
65963
- return a.prerelease.length === 0 ? 1 : -1;
65964
- }
65965
- const length = Math.max(a.prerelease.length, b.prerelease.length);
65966
- for (let index = 0;index < length; index += 1) {
65967
- const aPart = a.prerelease[index];
65968
- const bPart = b.prerelease[index];
65969
- if (aPart === undefined || bPart === undefined)
65970
- return aPart === undefined ? -1 : 1;
65971
- if (aPart === bPart)
65972
- continue;
65973
- const aNumber = numericIdentifier(aPart);
65974
- const bNumber = numericIdentifier(bPart);
65975
- if (aNumber !== undefined && bNumber !== undefined)
65976
- return aNumber < bNumber ? -1 : 1;
65977
- if (aNumber !== undefined || bNumber !== undefined)
65978
- return aNumber !== undefined ? -1 : 1;
65979
- return aPart < bPart ? -1 : 1;
65980
- }
65981
- return 0;
65982
- }
65983
66940
  function readUpdateCache(path = updateCachePath()) {
65984
66941
  try {
65985
- ensurePrivateDirectory(dirname10(path), "update cache directory");
66942
+ ensurePrivateDirectory(dirname11(path), "update cache directory");
65986
66943
  ensurePrivateRegularFileIfExists(path, "update cache");
65987
66944
  const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, UPDATE_RESPONSE_MAX_BYTES, "update cache"));
65988
66945
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
@@ -65990,7 +66947,7 @@ function readUpdateCache(path = updateCachePath()) {
65990
66947
  const value = parsed;
65991
66948
  if (typeof value.checkedAt !== "number" || !Number.isFinite(value.checkedAt))
65992
66949
  return;
65993
- if (typeof value.latestVersion !== "string" || !parseSemver(value.latestVersion))
66950
+ if (typeof value.latestVersion !== "string" || !isSemver(value.latestVersion))
65994
66951
  return;
65995
66952
  return {
65996
66953
  checkedAt: value.checkedAt,
@@ -66001,13 +66958,13 @@ function readUpdateCache(path = updateCachePath()) {
66001
66958
  }
66002
66959
  }
66003
66960
  function updateCachePath() {
66004
- return join31(globalDataDir(), "update.json");
66961
+ return join33(globalDataDir(), "update.json");
66005
66962
  }
66006
66963
  function readCurrentVersion() {
66007
66964
  try {
66008
- const packagePath = join31(import.meta.dir, "..", "..", "package.json");
66965
+ const packagePath = join33(import.meta.dir, "..", "..", "package.json");
66009
66966
  const parsed = JSON.parse(readBoundedFileTextSync(packagePath, 1024 * 1024, "package metadata"));
66010
- return typeof parsed.version === "string" && parseSemver(parsed.version) ? parsed.version : undefined;
66967
+ return typeof parsed.version === "string" && isSemver(parsed.version) ? parsed.version : undefined;
66011
66968
  } catch {
66012
66969
  return;
66013
66970
  }
@@ -66043,7 +67000,7 @@ async function fetchLatestVersion(fetcher, timeoutMs) {
66043
67000
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
66044
67001
  throw new Error("invalid npm registry response");
66045
67002
  const version = parsed.version;
66046
- if (typeof version !== "string" || !parseSemver(version))
67003
+ if (typeof version !== "string" || !isSemver(version))
66047
67004
  throw new Error("invalid npm package version");
66048
67005
  return version;
66049
67006
  } finally {
@@ -66052,7 +67009,7 @@ async function fetchLatestVersion(fetcher, timeoutMs) {
66052
67009
  }
66053
67010
  function writeUpdateCache(path, cache) {
66054
67011
  try {
66055
- ensurePrivateDirectory(dirname10(path), "update cache directory");
67012
+ ensurePrivateDirectory(dirname11(path), "update cache directory");
66056
67013
  ensurePrivateRegularFileIfExists(path, "update cache");
66057
67014
  atomicWriteFile(path, `${JSON.stringify(cache)}
66058
67015
  `, 384);
@@ -66068,20 +67025,6 @@ function updateCheckDisabled() {
66068
67025
  function envEnabled(value) {
66069
67026
  return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
66070
67027
  }
66071
- function parseSemver(value) {
66072
- const match = value.trim().match(/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
66073
- if (!match)
66074
- return;
66075
- return {
66076
- core: [Number(match[1]), Number(match[2]), Number(match[3])],
66077
- prerelease: match[4]?.split(".") ?? []
66078
- };
66079
- }
66080
- function numericIdentifier(value) {
66081
- if (!/^(0|[1-9]\d*)$/.test(value))
66082
- return;
66083
- return Number(value);
66084
- }
66085
67028
  var UPDATE_CACHE_TTL_MS, UPDATE_CHECK_TIMEOUT_MS = 4000, UPDATE_REGISTRY_URL = "https://registry.npmjs.org/farai/latest", UPDATE_RESPONSE_MAX_BYTES;
66086
67029
  var init_update_check = __esm(() => {
66087
67030
  init_config();
@@ -66148,8 +67091,8 @@ async function runOpenTui(input) {
66148
67091
  const renderer = managedRenderer.renderer;
66149
67092
  const updateCheck = prepareUpdateCheck();
66150
67093
  let done;
66151
- const finished = new Promise((resolve10) => {
66152
- done = resolve10;
67094
+ const finished = new Promise((resolve11) => {
67095
+ done = resolve11;
66153
67096
  });
66154
67097
  let exitPromise;
66155
67098
  const onSigint = () => {
@@ -66390,6 +67333,80 @@ no sessions are available in this workspace`}`);
66390
67333
  };
66391
67334
  });
66392
67335
 
67336
+ // src/agent-content/command.ts
67337
+ var exports_command2 = {};
67338
+ __export(exports_command2, {
67339
+ runContentUpdateCommand: () => runContentUpdateCommand
67340
+ });
67341
+ import { existsSync as existsSync26 } from "fs";
67342
+ async function runContentUpdateCommand(parsed, workspace) {
67343
+ if (parsed.kind === "status") {
67344
+ const status3 = contentStatus();
67345
+ if (!status3.active) {
67346
+ const knowledge = legacyKnowledgeDbPath();
67347
+ console.log("content: bundled defaults");
67348
+ console.log("active release: none");
67349
+ console.log(`knowledge: ${existsSync26(knowledge) ? knowledge : "not installed"}`);
67350
+ console.log("skills: bundled");
67351
+ return 0;
67352
+ }
67353
+ console.log(`content: ${status3.active.version}`);
67354
+ console.log(`activated: ${status3.active.activatedAt}`);
67355
+ console.log(`knowledge: ${status3.knowledgePath ?? "local fallback"}`);
67356
+ console.log(`skills: ${status3.skillsPath ?? "bundled fallback"}`);
67357
+ console.log(`available versions: ${status3.versions.join(", ") || "none"}`);
67358
+ return 0;
67359
+ }
67360
+ if (parsed.kind === "rollback") {
67361
+ const result2 = rollbackContentUpdate();
67362
+ console.log(`rolled back farai content to ${result2.version}`);
67363
+ return 0;
67364
+ }
67365
+ const status2 = await checkContentUpdate({
67366
+ workspace,
67367
+ force: true
67368
+ });
67369
+ if (parsed.kind === "check")
67370
+ return printContentUpdateStatus(status2);
67371
+ if (status2.state === "up_to_date") {
67372
+ console.log(`farai content is up to date${status2.active ? ` (${status2.active.version})` : ""}`);
67373
+ return 0;
67374
+ }
67375
+ if (status2.state !== "update_available" || !status2.manifest)
67376
+ return printContentUpdateStatus(status2);
67377
+ const result = await applyContentUpdate(status2.manifest, status2.manifestUrl);
67378
+ console.log(`updated farai content to ${result.version}`);
67379
+ return 0;
67380
+ }
67381
+ function printContentUpdateStatus(status2) {
67382
+ if (status2.state === "update_available") {
67383
+ console.log(`content update available: ${status2.active?.version ?? "none"} -> ${status2.manifest?.contentVersion}`);
67384
+ return 0;
67385
+ }
67386
+ if (status2.state === "up_to_date") {
67387
+ console.log(`farai content is up to date${status2.active ? ` (${status2.active.version})` : ""}`);
67388
+ return 0;
67389
+ }
67390
+ if (status2.state === "disabled") {
67391
+ console.log("farai content updates are disabled");
67392
+ return 0;
67393
+ }
67394
+ if (status2.state === "unavailable") {
67395
+ console.log("the content channel has no published artifacts yet");
67396
+ return 0;
67397
+ }
67398
+ if (status2.state === "incompatible") {
67399
+ console.error(`content ${status2.manifest?.contentVersion} requires farai ${status2.manifest?.minFaraiVersion} or newer`);
67400
+ return 1;
67401
+ }
67402
+ console.error(`content update check failed: ${status2.error ?? "unknown error"}`);
67403
+ return 1;
67404
+ }
67405
+ var init_command2 = __esm(() => {
67406
+ init_paths3();
67407
+ init_updater();
67408
+ });
67409
+
66393
67410
  // src/agent-benchmark/csi-cybench-33.ts
66394
67411
  var figureTimeout = (minutes, line) => ({
66395
67412
  status: "verified",
@@ -66651,40 +67668,40 @@ var init_csi_cybench_33 = __esm(() => {
66651
67668
  });
66652
67669
 
66653
67670
  // src/agent-benchmark/hash.ts
66654
- import { createHash as createHash12 } from "crypto";
66655
- import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync10, openSync as openSync6, readSync as readSync2, readdirSync as readdirSync11 } from "fs";
66656
- import { join as join32 } from "path";
67671
+ import { createHash as createHash13 } from "crypto";
67672
+ import { closeSync as closeSync7, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync11, openSync as openSync7, readSync as readSync3, readdirSync as readdirSync12 } from "fs";
67673
+ import { join as join34 } from "path";
66657
67674
  function stableStringify(value) {
66658
67675
  return JSON.stringify(sortValue(value));
66659
67676
  }
66660
67677
  function sha256(value) {
66661
- return createHash12("sha256").update(value).digest("hex");
67678
+ return createHash13("sha256").update(value).digest("hex");
66662
67679
  }
66663
67680
  function hashPath(path) {
66664
- const stat = lstatSync10(path);
67681
+ const stat = lstatSync11(path);
66665
67682
  if (stat.isFile())
66666
- return hashFile(path);
67683
+ return hashFile2(path);
66667
67684
  if (!stat.isDirectory())
66668
67685
  throw new Error(`unsupported benchmark input type: ${path}`);
66669
- const hash = createHash12("sha256");
67686
+ const hash = createHash13("sha256");
66670
67687
  hash.update("farai-directory-v2\x00");
66671
67688
  hashDirectory(path, Buffer.alloc(0), hash);
66672
67689
  return hash.digest("hex");
66673
67690
  }
66674
- function hashFile(path) {
67691
+ function hashFile2(path) {
66675
67692
  return hashFileDetails(path).digest;
66676
67693
  }
66677
67694
  function hashFileDetails(path) {
66678
- const descriptor = openSync6(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
67695
+ const descriptor = openSync7(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
66679
67696
  try {
66680
67697
  const before = fstatSync3(descriptor);
66681
67698
  if (!before.isFile())
66682
67699
  throw new Error(`unsupported benchmark input type: ${path}`);
66683
- const hash = createHash12("sha256");
67700
+ const hash = createHash13("sha256");
66684
67701
  let remaining2 = before.size;
66685
67702
  while (remaining2 > 0) {
66686
67703
  const chunk = Buffer.allocUnsafe(Math.min(1024 * 1024, remaining2));
66687
- const count2 = readSync2(descriptor, chunk, 0, chunk.length, null);
67704
+ const count2 = readSync3(descriptor, chunk, 0, chunk.length, null);
66688
67705
  if (count2 === 0)
66689
67706
  throw new Error(`benchmark input changed while hashing: ${path}`);
66690
67707
  hash.update(chunk.subarray(0, count2));
@@ -66700,7 +67717,7 @@ function hashFileDetails(path) {
66700
67717
  size: before.size
66701
67718
  };
66702
67719
  } finally {
66703
- closeSync6(descriptor);
67720
+ closeSync7(descriptor);
66704
67721
  }
66705
67722
  }
66706
67723
  function canonicalBenchmarkManifest(manifest) {
@@ -66755,22 +67772,22 @@ function sortValue(value) {
66755
67772
  return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, sortValue(item)]));
66756
67773
  }
66757
67774
  function hashDirectory(path, localPath, hash) {
66758
- const before = lstatSync10(path);
67775
+ const before = lstatSync11(path);
66759
67776
  if (!before.isDirectory() || before.isSymbolicLink())
66760
67777
  throw new Error(`unsupported benchmark input type: ${path}`);
66761
67778
  hash.update("directory\x00");
66762
67779
  hash.update(localPath);
66763
67780
  hash.update(`\x00${before.mode & 511}\x00`);
66764
- const names = readdirSync11(path, {
67781
+ const names = readdirSync12(path, {
66765
67782
  encoding: "buffer"
66766
67783
  }).map((name) => Buffer.from(name)).sort(Buffer.compare);
66767
67784
  for (const encodedName of names) {
66768
67785
  const name = encodedName.toString("utf8");
66769
67786
  if (!Buffer.from(name, "utf8").equals(encodedName))
66770
67787
  throw new Error(`benchmark input path is not valid utf-8: ${path}`);
66771
- const childPath = join32(path, name);
67788
+ const childPath = join34(path, name);
66772
67789
  const childLocalPath = localPath.length === 0 ? encodedName : Buffer.concat([localPath, Buffer.from("/"), encodedName]);
66773
- const stat = lstatSync10(childPath);
67790
+ const stat = lstatSync11(childPath);
66774
67791
  if (stat.isDirectory() && !stat.isSymbolicLink()) {
66775
67792
  hashDirectory(childPath, childLocalPath, hash);
66776
67793
  continue;
@@ -66782,7 +67799,7 @@ function hashDirectory(path, localPath, hash) {
66782
67799
  hash.update(childLocalPath);
66783
67800
  hash.update(`\x00${file.mode}\x00${file.size}\x00${file.digest}\x00`);
66784
67801
  }
66785
- const after = lstatSync10(path);
67802
+ const after = lstatSync11(path);
66786
67803
  if (!after.isDirectory() || after.dev !== before.dev || after.ino !== before.ino || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) {
66787
67804
  throw new Error(`benchmark input changed while hashing: ${path}`);
66788
67805
  }
@@ -66790,7 +67807,7 @@ function hashDirectory(path, localPath, hash) {
66790
67807
  var init_hash = () => {};
66791
67808
 
66792
67809
  // src/agent-benchmark/manifest.ts
66793
- import { isAbsolute as isAbsolute7, normalize as normalize3 } from "path";
67810
+ import { isAbsolute as isAbsolute8, normalize as normalize3 } from "path";
66794
67811
  async function loadBenchmarkManifest(path) {
66795
67812
  return normalizeBenchmarkManifest(JSON.parse(await readBoundedFileText(path, BENCHMARK_MANIFEST_MAX_BYTES, "benchmark manifest")));
66796
67813
  }
@@ -66829,27 +67846,27 @@ function normalizeBenchmarkManifest(value) {
66829
67846
  id: requiredString(suite.id, "suite.id"),
66830
67847
  version: requiredString(suite.version, "suite.version"),
66831
67848
  source: requiredString(suite.source, "suite.source"),
66832
- ...optionalString2(suite.sourceDigest ?? suite.source_digest) ? {
66833
- sourceDigest: optionalString2(suite.sourceDigest ?? suite.source_digest)
67849
+ ...optionalString3(suite.sourceDigest ?? suite.source_digest) ? {
67850
+ sourceDigest: optionalString3(suite.sourceDigest ?? suite.source_digest)
66834
67851
  } : {}
66835
67852
  },
66836
67853
  challenge: {
66837
67854
  id: requiredString(challenge.id, "challenge.id"),
66838
67855
  prompt: requiredString(challenge.prompt, "challenge.prompt"),
66839
- ...optionalString2(challenge.category) ? {
66840
- category: optionalString2(challenge.category)
67856
+ ...optionalString3(challenge.category) ? {
67857
+ category: optionalString3(challenge.category)
66841
67858
  } : {},
66842
- ...optionalString2(challenge.difficulty) ? {
66843
- difficulty: optionalString2(challenge.difficulty)
67859
+ ...optionalString3(challenge.difficulty) ? {
67860
+ difficulty: optionalString3(challenge.difficulty)
66844
67861
  } : {},
66845
- ...optionalString2(challenge.source) ? {
66846
- source: optionalString2(challenge.source)
67862
+ ...optionalString3(challenge.source) ? {
67863
+ source: optionalString3(challenge.source)
66847
67864
  } : {},
66848
- ...optionalString2(challenge.targetImage ?? challenge.target_image) ? {
66849
- targetImage: optionalString2(challenge.targetImage ?? challenge.target_image)
67865
+ ...optionalString3(challenge.targetImage ?? challenge.target_image) ? {
67866
+ targetImage: optionalString3(challenge.targetImage ?? challenge.target_image)
66850
67867
  } : {},
66851
- ...optionalString2(challenge.targetImageDigest ?? challenge.target_image_digest) ? {
66852
- targetImageDigest: optionalString2(challenge.targetImageDigest ?? challenge.target_image_digest)
67868
+ ...optionalString3(challenge.targetImageDigest ?? challenge.target_image_digest) ? {
67869
+ targetImageDigest: optionalString3(challenge.targetImageDigest ?? challenge.target_image_digest)
66853
67870
  } : {},
66854
67871
  ...challenge.targetCommand ?? challenge.target_command ? {
66855
67872
  targetCommand: stringArray3(challenge.targetCommand ?? challenge.target_command, "challenge.targetCommand")
@@ -66857,11 +67874,11 @@ function normalizeBenchmarkManifest(value) {
66857
67874
  },
66858
67875
  model: {
66859
67876
  selection: requiredString(model.selection, "model.selection"),
66860
- ...optionalString2(model.provider) ? {
66861
- provider: optionalString2(model.provider)
67877
+ ...optionalString3(model.provider) ? {
67878
+ provider: optionalString3(model.provider)
66862
67879
  } : {},
66863
- ...optionalString2(model.protocol) ? {
66864
- protocol: optionalString2(model.protocol)
67880
+ ...optionalString3(model.protocol) ? {
67881
+ protocol: optionalString3(model.protocol)
66865
67882
  } : {},
66866
67883
  ...optionalPositiveNumber(model.contextWindow ?? model.context_window, "model.contextWindow") ? {
66867
67884
  contextWindow: optionalPositiveNumber(model.contextWindow ?? model.context_window, "model.contextWindow")
@@ -66920,7 +67937,7 @@ function normalizeBenchmarkSuiteManifest(value) {
66920
67937
  const id2 = requiredString(raw.id, "id");
66921
67938
  const version = requiredString(raw.version, "version");
66922
67939
  const source = requiredString(raw.source, "source");
66923
- const sourceDigest = optionalString2(raw.sourceDigest ?? raw.source_digest);
67940
+ const sourceDigest = optionalString3(raw.sourceDigest ?? raw.source_digest);
66924
67941
  const repetitions = positiveInteger4(raw.repetitions, "repetitions");
66925
67942
  const concurrency = positiveInteger4(raw.concurrency, "concurrency");
66926
67943
  const runs = raw.runs.map((entry, index) => {
@@ -66977,8 +67994,8 @@ function normalizeOracle(value) {
66977
67994
  executableSha256: optionalSha256(raw.executableSha256 ?? raw.executable_sha256, "oracle.executableSha256")
66978
67995
  } : {},
66979
67996
  flagPattern: requiredString(raw.flagPattern ?? raw.flag_pattern, "oracle.flagPattern"),
66980
- ...optionalString2(raw.flags) ? {
66981
- flags: optionalString2(raw.flags)
67997
+ ...optionalString3(raw.flags) ? {
67998
+ flags: optionalString3(raw.flags)
66982
67999
  } : {},
66983
68000
  ...optionalPositiveNumber(raw.timeoutSeconds ?? raw.timeout_seconds, "oracle.timeoutSeconds") ? {
66984
68001
  timeoutSeconds: optionalPositiveNumber(raw.timeoutSeconds ?? raw.timeout_seconds, "oracle.timeoutSeconds")
@@ -67034,7 +68051,7 @@ function optionalResource(raw, key, integer2, snake = key) {
67034
68051
  };
67035
68052
  }
67036
68053
  function safeRelativePath(value) {
67037
- if (isAbsolute7(value))
68054
+ if (isAbsolute8(value))
67038
68055
  throw new Error(`benchmark destination must be relative: ${value}`);
67039
68056
  const normalized = normalize3(value).replace(/\\/g, "/");
67040
68057
  if (!normalized || normalized === "." || normalized.split("/").includes(".."))
@@ -67066,7 +68083,7 @@ function requiredString(value, name) {
67066
68083
  throw new Error(`${name} must be a non-empty string`);
67067
68084
  return value.trim();
67068
68085
  }
67069
- function optionalString2(value) {
68086
+ function optionalString3(value) {
67070
68087
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
67071
68088
  }
67072
68089
  function optionalSha256(value, name) {
@@ -67113,7 +68130,7 @@ function optionalInteger(value, name, minimum) {
67113
68130
  return number;
67114
68131
  }
67115
68132
  var BENCHMARK_MANIFEST_MAX_BYTES;
67116
- var init_manifest = __esm(() => {
68133
+ var init_manifest2 = __esm(() => {
67117
68134
  init_file_read();
67118
68135
  BENCHMARK_MANIFEST_MAX_BYTES = 32 * 1024 * 1024;
67119
68136
  });
@@ -67128,17 +68145,17 @@ __export(exports_csi_suite, {
67128
68145
  loadCsiCampaignConfig: () => loadCsiCampaignConfig,
67129
68146
  generateCsiBenchmarkSuite: () => generateCsiBenchmarkSuite
67130
68147
  });
67131
- import { existsSync as existsSync24, readdirSync as readdirSync12, statSync as statSync6 } from "fs";
67132
- import { dirname as dirname11, isAbsolute as isAbsolute8, join as join33, relative as relative11, resolve as resolve10 } from "path";
68148
+ import { existsSync as existsSync27, readdirSync as readdirSync13, statSync as statSync7 } from "fs";
68149
+ import { dirname as dirname12, isAbsolute as isAbsolute9, join as join35, relative as relative11, resolve as resolve11 } from "path";
67133
68150
  async function loadCsiCampaignConfig(path) {
67134
68151
  return normalizeCsiCampaignConfig(JSON.parse(await readBoundedFileText(path, CSI_CAMPAIGN_MAX_BYTES, "csi campaign config")));
67135
68152
  }
67136
68153
  async function loadCsiMaterialIndex(root) {
67137
- return normalizeCsiMaterialIndex(JSON.parse(await readBoundedFileText(resolve10(root, "index.json"), CSI_MATERIAL_INDEX_MAX_BYTES, "csi material index")));
68154
+ return normalizeCsiMaterialIndex(JSON.parse(await readBoundedFileText(resolve11(root, "index.json"), CSI_MATERIAL_INDEX_MAX_BYTES, "csi material index")));
67138
68155
  }
67139
68156
  async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67140
68157
  const config = normalizeCsiCampaignConfig(configInput);
67141
- const root = resolve10(materialRoot);
68158
+ const root = resolve11(materialRoot);
67142
68159
  const materials = await loadCsiMaterialIndex(root);
67143
68160
  const selectedIds = config.challenges ?? CSI_CYBENCH_33.challenges.map((challenge) => challenge.id);
67144
68161
  const selected = selectedIds.map((id2) => {
@@ -67154,16 +68171,16 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67154
68171
  if (config.isolation.backend === "host" && material.requiresTarget)
67155
68172
  throw new Error(`host csi challenge requires a live target and cannot run in host smoke mode: ${challenge.id}`);
67156
68173
  const promptPath = protectedPath(root, material.promptFile, `${challenge.id}.promptFile`);
67157
- if (!existsSync24(promptPath) || !statSync6(promptPath).isFile())
68174
+ if (!existsSync27(promptPath) || !statSync7(promptPath).isFile())
67158
68175
  throw new Error(`missing prompt file for csi challenge: ${challenge.id}`);
67159
68176
  const prompt = readBoundedFileTextSync(promptPath, CSI_PROMPT_MAX_BYTES, `csi prompt ${challenge.id}`).trim();
67160
68177
  if (!prompt)
67161
68178
  throw new Error(`empty prompt file for csi challenge: ${challenge.id}`);
67162
68179
  const files = material.files?.map((file, index) => {
67163
68180
  const source = protectedPath(root, file.source, `${challenge.id}.files[${index}].source`);
67164
- if (!existsSync24(source))
68181
+ if (!existsSync27(source))
67165
68182
  throw new Error(`missing input for csi challenge ${challenge.id}: ${file.source}`);
67166
- if (statSync6(source).isDirectory() && !listFiles(source).length)
68183
+ if (statSync7(source).isDirectory() && !listFiles(source).length)
67167
68184
  throw new Error(`empty input directory for csi challenge ${challenge.id}: ${file.source}`);
67168
68185
  const digest2 = hashPath(source);
67169
68186
  if (file.sha256 && file.sha256.toLowerCase() !== digest2)
@@ -67183,14 +68200,14 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67183
68200
  throw new Error(`missing required protected files for csi challenge ${challenge.id}: ${missing.join(", ")}`);
67184
68201
  }
67185
68202
  const executable = protectedPath(root, material.oracle.executable, `${challenge.id}.oracle.executable`);
67186
- if (!existsSync24(executable) || !statSync6(executable).isFile())
68203
+ if (!existsSync27(executable) || !statSync7(executable).isFile())
67187
68204
  throw new Error(`missing oracle executable for csi challenge: ${challenge.id}`);
67188
- if ((statSync6(executable).mode & 73) === 0)
68205
+ if ((statSync7(executable).mode & 73) === 0)
67189
68206
  throw new Error(`oracle executable is not executable for csi challenge: ${challenge.id}`);
67190
68207
  const antiCheatExecutable = material.antiCheat ? protectedPath(root, material.antiCheat.executable, `${challenge.id}.antiCheat.executable`) : undefined;
67191
- if (antiCheatExecutable && (!existsSync24(antiCheatExecutable) || !statSync6(antiCheatExecutable).isFile()))
68208
+ if (antiCheatExecutable && (!existsSync27(antiCheatExecutable) || !statSync7(antiCheatExecutable).isFile()))
67192
68209
  throw new Error(`missing anti-cheat executable for csi challenge: ${challenge.id}`);
67193
- if (antiCheatExecutable && (statSync6(antiCheatExecutable).mode & 73) === 0)
68210
+ if (antiCheatExecutable && (statSync7(antiCheatExecutable).mode & 73) === 0)
67194
68211
  throw new Error(`anti-cheat executable is not executable for csi challenge: ${challenge.id}`);
67195
68212
  if (config.isolation.backend === "docker" && !material.target)
67196
68213
  throw new Error(`docker csi challenge requires a pinned target image: ${challenge.id}`);
@@ -67263,8 +68280,8 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67263
68280
  });
67264
68281
  }
67265
68282
  function writeCsiBenchmarkSuite(suite, path) {
67266
- const directory = dirname11(resolve10(path));
67267
- if (!existsSync24(directory))
68283
+ const directory = dirname12(resolve11(path));
68284
+ if (!existsSync27(directory))
67268
68285
  throw new Error(`suite output directory does not exist: ${directory}`);
67269
68286
  atomicWriteFile(path, `${JSON.stringify(suite, null, 2)}
67270
68287
  `, 384);
@@ -67343,8 +68360,8 @@ function normalizeCsiMaterialIndex(value) {
67343
68360
  args: stringArray4(oracle.args, `challenges.${id2}.oracle.args`, true)
67344
68361
  },
67345
68362
  flagPattern: requiredString2(oracle.flagPattern ?? oracle.flag_pattern, `challenges.${id2}.oracle.flagPattern`),
67346
- ...optionalString3(oracle.flags) ? {
67347
- flags: optionalString3(oracle.flags)
68363
+ ...optionalString4(oracle.flags) ? {
68364
+ flags: optionalString4(oracle.flags)
67348
68365
  } : {},
67349
68366
  ...oracle.timeoutSeconds ?? oracle.timeout_seconds ? {
67350
68367
  timeoutSeconds: positiveNumber3(oracle.timeoutSeconds ?? oracle.timeout_seconds, `challenges.${id2}.oracle.timeoutSeconds`)
@@ -67390,18 +68407,18 @@ function resolveTimeoutMinutes(id2, catalog, material) {
67390
68407
  return material.minutes;
67391
68408
  }
67392
68409
  function protectedPath(root, path, name) {
67393
- if (isAbsolute8(path))
68410
+ if (isAbsolute9(path))
67394
68411
  throw new Error(`${name} must be relative to the protected material root`);
67395
- const resolved = resolve10(root, path);
68412
+ const resolved = resolve11(root, path);
67396
68413
  const difference = relative11(root, resolved);
67397
- if (!difference || difference.startsWith("..") || isAbsolute8(difference))
68414
+ if (!difference || difference.startsWith("..") || isAbsolute9(difference))
67398
68415
  throw new Error(`${name} escapes the protected material root`);
67399
68416
  return resolved;
67400
68417
  }
67401
68418
  function listFiles(rootPath) {
67402
- if (!statSync6(rootPath).isDirectory())
68419
+ if (!statSync7(rootPath).isDirectory())
67403
68420
  return [rootPath];
67404
- return readdirSync12(rootPath).flatMap((name) => listFiles(join33(rootPath, name)));
68421
+ return readdirSync13(rootPath).flatMap((name) => listFiles(join35(rootPath, name)));
67405
68422
  }
67406
68423
  function object2(value, name) {
67407
68424
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -67413,7 +68430,7 @@ function requiredString2(value, name) {
67413
68430
  throw new Error(`${name} must be a non-empty string`);
67414
68431
  return value.trim();
67415
68432
  }
67416
- function optionalString3(value) {
68433
+ function optionalString4(value) {
67417
68434
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
67418
68435
  }
67419
68436
  function stringArray4(value, name, allowEmpty) {
@@ -67455,7 +68472,7 @@ var CSI_CAMPAIGN_MAX_BYTES, CSI_MATERIAL_INDEX_MAX_BYTES, CSI_PROMPT_MAX_BYTES;
67455
68472
  var init_csi_suite = __esm(() => {
67456
68473
  init_csi_cybench_33();
67457
68474
  init_hash();
67458
- init_manifest();
68475
+ init_manifest2();
67459
68476
  init_file_read();
67460
68477
  init_atomic_file();
67461
68478
  CSI_CAMPAIGN_MAX_BYTES = 4 * 1024 * 1024;
@@ -67464,28 +68481,28 @@ var init_csi_suite = __esm(() => {
67464
68481
  });
67465
68482
 
67466
68483
  // src/agent-benchmark/bundle.ts
67467
- import { createHash as createHash13 } from "crypto";
67468
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync11 } from "fs";
67469
- import { join as join34 } from "path";
68484
+ import { createHash as createHash14 } from "crypto";
68485
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync12 } from "fs";
68486
+ import { join as join36 } from "path";
67470
68487
  function writeBenchmarkBundle(bundle, directory) {
67471
- mkdirSync11(directory, {
68488
+ mkdirSync12(directory, {
67472
68489
  recursive: true
67473
68490
  });
67474
68491
  const files = new Map([["manifest.json", json(redactManifest(bundle.manifest))], ["result.json", json(bundle.result)], ["environment.json", json(bundle.result.frozen)], ["sessions.jsonl", jsonl(bundle.sessions)], ["turns.jsonl", jsonl(bundle.turns)], ["messages.jsonl", jsonl(bundle.messages)], ["events.jsonl", jsonl(bundle.events)], ["tool-calls.jsonl", jsonl(bundle.toolCalls)], ["jobs.jsonl", jsonl(bundle.jobs)], ["usage.jsonl", jsonl(bundle.usage)], ["compactions.jsonl", jsonl(bundle.compactions)], ["evidence.jsonl", jsonl(bundle.evidence)]]);
67475
68492
  for (const [name, content] of files)
67476
- atomicWriteFile(join34(directory, name), content, 384);
68493
+ atomicWriteFile(join36(directory, name), content, 384);
67477
68494
  const checksums = [...files.keys()].sort().map((name) => `${sha2562(files.get(name))} ${name}`).join(`
67478
68495
  `);
67479
- atomicWriteFile(join34(directory, "checksums.sha256"), `${checksums}
68496
+ atomicWriteFile(join36(directory, "checksums.sha256"), `${checksums}
67480
68497
  `, 384);
67481
68498
  for (const name of [...files.keys(), "checksums.sha256"])
67482
- chmodSync3(join34(directory, name), 292);
68499
+ chmodSync3(join36(directory, name), 292);
67483
68500
  return directory;
67484
68501
  }
67485
68502
  function writeBenchmarkResult(result, path) {
67486
68503
  const directory = path.slice(0, Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")));
67487
68504
  if (directory)
67488
- mkdirSync11(directory, {
68505
+ mkdirSync12(directory, {
67489
68506
  recursive: true
67490
68507
  });
67491
68508
  atomicWriteFile(path, json(result), 384);
@@ -67503,7 +68520,7 @@ function jsonl(values) {
67503
68520
  ` : "";
67504
68521
  }
67505
68522
  function sha2562(value) {
67506
- return createHash13("sha256").update(value).digest("hex");
68523
+ return createHash14("sha256").update(value).digest("hex");
67507
68524
  }
67508
68525
  var init_bundle = __esm(() => {
67509
68526
  init_hash();
@@ -67511,8 +68528,8 @@ var init_bundle = __esm(() => {
67511
68528
  });
67512
68529
 
67513
68530
  // src/agent-benchmark/docker-lifecycle.ts
67514
- import { existsSync as existsSync25 } from "fs";
67515
- import { resolve as resolve11 } from "path";
68531
+ import { existsSync as existsSync28 } from "fs";
68532
+ import { resolve as resolve12 } from "path";
67516
68533
 
67517
68534
  class BenchmarkDockerLifecycle {
67518
68535
  constructor(manifest, workspace, runId, runner = runProcess2) {
@@ -67630,7 +68647,7 @@ function buildBenchmarkDockerPlan(manifest, workspace, runId, agentImageId) {
67630
68647
  throw new Error("docker benchmark requires a pinned target image");
67631
68648
  if (!manifest.antiCheat)
67632
68649
  throw new Error("docker benchmark requires an external anti-cheat hook");
67633
- if (!existsSync25(manifest.antiCheat.executable))
68650
+ if (!existsSync28(manifest.antiCheat.executable))
67634
68651
  throw new Error("anti-cheat executable is missing");
67635
68652
  if (hashPath(manifest.antiCheat.executable) !== manifest.antiCheat.executableSha256)
67636
68653
  throw new Error("anti-cheat executable hash mismatch");
@@ -67649,7 +68666,7 @@ function buildBenchmarkDockerPlan(manifest, workspace, runId, agentImageId) {
67649
68666
  const resourceArgs = [...resources?.cpus ? ["--cpus", String(resources.cpus)] : [], ...resources?.memoryMb ? ["--memory", `${resources.memoryMb}m`] : []];
67650
68667
  const targetImage = pinnedImage(manifest.challenge.targetImage, manifest.challenge.targetImageDigest);
67651
68668
  const targetStart = ["run", "-d", "--name", names.target, "--network", names.network, "--network-alias", "target", "--cap-drop", "ALL", "--cap-add", "NET_BIND_SERVICE", ...common, ...resourceArgs, targetImage, ...manifest.challenge.targetCommand ?? []];
67652
- const resolvedWorkspace = resolve11(workspace);
68669
+ const resolvedWorkspace = resolve12(workspace);
67653
68670
  const agentStart = ["run", "-d", "--name", names.agent, "--network", names.network, "--workdir", "/workspace", "--volume", `${resolvedWorkspace}:/workspace:rw`, "--volume", "/workspace/.farai", "--read-only", "--tmpfs", "/tmp:rw,nosuid,nodev,size=512m", "--tmpfs", "/root:rw,nosuid,nodev,size=256m", "--tmpfs", "/run:rw,nosuid,nodev,size=64m", "--cap-drop", "ALL", "--cap-add", "NET_ADMIN", "--cap-add", "NET_RAW", ...common, ...resourceArgs, agentImageId, "sleep", "infinity"];
67654
68671
  return {
67655
68672
  names,
@@ -67699,9 +68716,9 @@ var init_docker_lifecycle = __esm(() => {
67699
68716
  });
67700
68717
 
67701
68718
  // src/agent-benchmark/git-state.ts
67702
- import { createHash as createHash14 } from "crypto";
67703
- import { lstatSync as lstatSync11, readlinkSync } from "fs";
67704
- import { isAbsolute as isAbsolute9, relative as relative12, resolve as resolve12 } from "path";
68719
+ import { createHash as createHash15 } from "crypto";
68720
+ import { lstatSync as lstatSync12, readlinkSync } from "fs";
68721
+ import { isAbsolute as isAbsolute10, relative as relative12, resolve as resolve13 } from "path";
67705
68722
  import { spawn as spawn5 } from "child_process";
67706
68723
  async function freezeGitSourceState(root) {
67707
68724
  if (!await isGitWorktree(root))
@@ -67738,7 +68755,7 @@ async function readGitCommit(root) {
67738
68755
  return commit.toLowerCase();
67739
68756
  }
67740
68757
  async function hashGitWorktree(root, hasCommit) {
67741
- const hash = createHash14("sha256");
68758
+ const hash = createHash15("sha256");
67742
68759
  hash.update("farai-git-worktree-v2\x00");
67743
68760
  await hashCommand(root, ["status", "--porcelain=v1", "-z", "--untracked-files=no", "--ignore-submodules=none"], hash, "status");
67744
68761
  await hashCommand(root, ["diff", "--no-ext-diff", "--no-textconv", "--binary", "--full-index", "--submodule=diff", "--"], hash, "unstaged");
@@ -67780,17 +68797,17 @@ function hashUntrackedPath(root, encodedPath, hash) {
67780
68797
  const path = encodedPath.toString("utf8");
67781
68798
  if (!Buffer.from(path, "utf8").equals(encodedPath))
67782
68799
  throw new Error("git returned an untracked path that is not valid utf-8");
67783
- const absolute = resolve12(root, path);
67784
- const local = relative12(resolve12(root), absolute);
67785
- if (!path || isAbsolute9(path) || local === ".." || local.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute9(local)) {
68800
+ const absolute = resolve13(root, path);
68801
+ const local = relative12(resolve13(root), absolute);
68802
+ if (!path || isAbsolute10(path) || local === ".." || local.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute10(local)) {
67786
68803
  throw new Error(`git returned an unsafe untracked path: ${path}`);
67787
68804
  }
67788
- const stat = lstatSync11(absolute);
68805
+ const stat = lstatSync12(absolute);
67789
68806
  hash.update("path\x00");
67790
68807
  hash.update(encodedPath);
67791
68808
  hash.update("\x00");
67792
68809
  if (stat.isFile()) {
67793
- hash.update(`file\x00${stat.mode & 511}\x00${stat.size}\x00${hashFile(absolute)}\x00`);
68810
+ hash.update(`file\x00${stat.mode & 511}\x00${stat.size}\x00${hashFile2(absolute)}\x00`);
67794
68811
  return;
67795
68812
  }
67796
68813
  if (stat.isSymbolicLink()) {
@@ -67891,27 +68908,27 @@ __export(exports_runner, {
67891
68908
  normalizeBenchmarkManifest: () => normalizeBenchmarkManifest,
67892
68909
  loadBenchmarkManifest: () => loadBenchmarkManifest
67893
68910
  });
67894
- import { cpSync, existsSync as existsSync26, lstatSync as lstatSync12, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readdirSync as readdirSync13, renameSync as renameSync8, rmSync as rmSync10, rmdirSync } from "fs";
68911
+ import { cpSync, existsSync as existsSync29, lstatSync as lstatSync13, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readdirSync as readdirSync14, renameSync as renameSync9, rmSync as rmSync11, rmdirSync } from "fs";
67895
68912
  import { arch, platform, tmpdir as tmpdir4 } from "os";
67896
- import { dirname as dirname12, isAbsolute as isAbsolute10, join as join35, relative as relative13, resolve as resolve13 } from "path";
67897
- import { randomUUID as randomUUID10 } from "crypto";
68913
+ import { dirname as dirname13, isAbsolute as isAbsolute11, join as join37, relative as relative13, resolve as resolve14 } from "path";
68914
+ import { randomUUID as randomUUID11 } from "crypto";
67898
68915
  async function runBenchmark(input, options = {}) {
67899
68916
  const manifest = normalizeBenchmarkManifest(input);
67900
68917
  assertExecutableIsolation(manifest);
67901
- const workspace = options.workspace ?? mkdtempSync3(join35(tmpdir4(), "farai-benchmark-"));
67902
- const artifactsRoot = options.artifactsDir ?? mkdtempSync3(join35(tmpdir4(), "farai-benchmark-artifacts-"));
68918
+ const workspace = options.workspace ?? mkdtempSync3(join37(tmpdir4(), "farai-benchmark-"));
68919
+ const artifactsRoot = options.artifactsDir ?? mkdtempSync3(join37(tmpdir4(), "farai-benchmark-artifacts-"));
67903
68920
  const repetition = options.repetition ?? 1;
67904
- mkdirSync12(workspace, {
68921
+ mkdirSync13(workspace, {
67905
68922
  recursive: true
67906
68923
  });
67907
68924
  assertCleanWorkspace(workspace);
67908
68925
  assertArtifactsOutsideWorkspace(workspace, artifactsRoot);
67909
- mkdirSync12(artifactsRoot, {
68926
+ mkdirSync13(artifactsRoot, {
67910
68927
  recursive: true
67911
68928
  });
67912
68929
  stageFiles(manifest, workspace);
67913
68930
  const runId = id();
67914
- const bundlePath = join35(artifactsRoot, `${safeName2(manifest.challenge.id)}-r${repetition}-${runId}`);
68931
+ const bundlePath = join37(artifactsRoot, `${safeName2(manifest.challenge.id)}-r${repetition}-${runId}`);
67915
68932
  const provider = options.provider ?? await createChatProviderForSession(syntheticSession(workspace, manifest));
67916
68933
  assertProvider(manifest, provider);
67917
68934
  const dockerLifecycle = manifest.isolation.backend === "docker" ? new BenchmarkDockerLifecycle(manifest, workspace, runId, options.dockerProcessRunner) : undefined;
@@ -67955,7 +68972,7 @@ async function runBenchmark(input, options = {}) {
67955
68972
  session = runtime.updateSession(session.id, {
67956
68973
  toolScope: activeTools.map((tool) => tool.name)
67957
68974
  });
67958
- const faraiRoot = options.faraiRoot ?? resolve13(import.meta.dir, "..", "..");
68975
+ const faraiRoot = options.faraiRoot ?? resolve14(import.meta.dir, "..", "..");
67959
68976
  const frozen = await freezeRun(manifest, session, activeTools, faraiRoot, provider, dockerState?.agentImageId);
67960
68977
  const promptPromise = runtime.prompt(session, manifest.challenge.prompt).then((result2) => {
67961
68978
  response = result2.response;
@@ -68086,13 +69103,13 @@ function assertExecutableIsolation(manifest) {
68086
69103
  }
68087
69104
  }
68088
69105
  function assertCleanWorkspace(workspace) {
68089
- const entries = readdirSync13(workspace);
69106
+ const entries = readdirSync14(workspace);
68090
69107
  if (entries.length)
68091
69108
  throw new Error(`benchmark workspace must be an empty scratch directory: ${workspace}`);
68092
69109
  }
68093
69110
  function assertArtifactsOutsideWorkspace(workspace, artifactsRoot) {
68094
- const difference = relative13(resolve13(workspace), resolve13(artifactsRoot));
68095
- const reverse = relative13(resolve13(artifactsRoot), resolve13(workspace));
69111
+ const difference = relative13(resolve14(workspace), resolve14(artifactsRoot));
69112
+ const reverse = relative13(resolve14(artifactsRoot), resolve14(workspace));
68096
69113
  if (!difference || !difference.startsWith("..") || !reverse.startsWith("..")) {
68097
69114
  throw new Error("benchmark artifacts directory must be outside the scratch workspace");
68098
69115
  }
@@ -68189,18 +69206,18 @@ function collectRunData(runtime, rootSessionId) {
68189
69206
  };
68190
69207
  }
68191
69208
  function stageFiles(manifest, workspace) {
68192
- const root = resolve13(workspace);
68193
- const workspaceStat = lstatSync12(root);
69209
+ const root = resolve14(workspace);
69210
+ const workspaceStat = lstatSync13(root);
68194
69211
  if (!workspaceStat.isDirectory() || workspaceStat.isSymbolicLink())
68195
69212
  throw new Error("benchmark workspace must be a real directory");
68196
69213
  const inputs = (manifest.files ?? []).map((file) => {
68197
- const source = resolve13(file.source);
68198
- if (!existsSync26(source))
69214
+ const source = resolve14(file.source);
69215
+ if (!existsSync29(source))
68199
69216
  throw new Error(`benchmark input does not exist: ${file.source}`);
68200
69217
  const expectedHash = file.sha256?.toLowerCase() ?? hashPath(source);
68201
69218
  if (hashPath(source) !== expectedHash)
68202
69219
  throw new Error(`benchmark input hash mismatch: ${file.source}`);
68203
- const target = resolve13(root, file.destination);
69220
+ const target = resolve14(root, file.destination);
68204
69221
  if (!isDescendantPath(relative13(root, target)))
68205
69222
  throw new Error(`benchmark destination escapes scratch workspace: ${file.destination}`);
68206
69223
  return {
@@ -68222,10 +69239,10 @@ function stageFiles(manifest, workspace) {
68222
69239
  const createdDirectories = [];
68223
69240
  try {
68224
69241
  for (const input of inputs) {
68225
- ensureStagingParent(root, dirname12(input.target), createdDirectories);
68226
- const staged = join35(dirname12(input.target), `.${randomUUID10()}.farai-stage`);
69242
+ ensureStagingParent(root, dirname13(input.target), createdDirectories);
69243
+ const staged = join37(dirname13(input.target), `.${randomUUID11()}.farai-stage`);
68227
69244
  try {
68228
- const sourceStat = lstatSync12(input.source);
69245
+ const sourceStat = lstatSync13(input.source);
68229
69246
  cpSync(input.source, staged, {
68230
69247
  recursive: sourceStat.isDirectory(),
68231
69248
  dereference: false,
@@ -68237,14 +69254,14 @@ function stageFiles(manifest, workspace) {
68237
69254
  throw new Error(`benchmark input was not staged: ${input.file.destination}`);
68238
69255
  if (hashPath(staged) !== input.expectedHash)
68239
69256
  throw new Error(`benchmark input changed while staging: ${input.file.source}`);
68240
- if (lstatSync12(staged).isDirectory() && !listFiles2(staged).length)
69257
+ if (lstatSync13(staged).isDirectory() && !listFiles2(staged).length)
68241
69258
  throw new Error(`benchmark input staged an empty directory: ${input.file.destination}`);
68242
69259
  if (pathExists(input.target))
68243
69260
  throw new Error(`benchmark destinations must not overlap: ${input.file.destination}`);
68244
- renameSync8(staged, input.target);
69261
+ renameSync9(staged, input.target);
68245
69262
  published.push(input.target);
68246
69263
  } finally {
68247
- rmSync10(staged, {
69264
+ rmSync11(staged, {
68248
69265
  recursive: true,
68249
69266
  force: true
68250
69267
  });
@@ -68252,7 +69269,7 @@ function stageFiles(manifest, workspace) {
68252
69269
  }
68253
69270
  } catch (error) {
68254
69271
  for (const target of published.reverse())
68255
- rmSync10(target, {
69272
+ rmSync11(target, {
68256
69273
  recursive: true,
68257
69274
  force: true
68258
69275
  });
@@ -68272,42 +69289,42 @@ function pathsOverlap(left, right) {
68272
69289
  return isDescendantPath(leftToRight) || isDescendantPath(rightToLeft);
68273
69290
  }
68274
69291
  function isDescendantPath(value) {
68275
- return Boolean(value) && value !== ".." && !value.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute10(value);
69292
+ return Boolean(value) && value !== ".." && !value.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute11(value);
68276
69293
  }
68277
69294
  function listFiles2(rootPath) {
68278
- if (!existsSync26(rootPath))
69295
+ if (!existsSync29(rootPath))
68279
69296
  return [];
68280
- const stat = lstatSync12(rootPath);
69297
+ const stat = lstatSync13(rootPath);
68281
69298
  if (stat.isFile())
68282
69299
  return [rootPath];
68283
69300
  if (!stat.isDirectory())
68284
69301
  throw new Error(`unsupported staged benchmark input type: ${rootPath}`);
68285
- return readdirSync13(rootPath).flatMap((name) => listFiles2(join35(rootPath, name)));
69302
+ return readdirSync14(rootPath).flatMap((name) => listFiles2(join37(rootPath, name)));
68286
69303
  }
68287
69304
  function ensureStagingParent(workspace, parent, createdDirectories) {
68288
- const root = resolve13(workspace);
68289
- const local = relative13(root, resolve13(parent));
69305
+ const root = resolve14(workspace);
69306
+ const local = relative13(root, resolve14(parent));
68290
69307
  if (local === "")
68291
69308
  return;
68292
69309
  if (!isDescendantPath(local))
68293
69310
  throw new Error("benchmark staging parent escapes scratch workspace");
68294
69311
  let current = root;
68295
69312
  for (const segment of local.split(/[\\/]+/).filter(Boolean)) {
68296
- current = join35(current, segment);
69313
+ current = join37(current, segment);
68297
69314
  if (!pathExists(current)) {
68298
- mkdirSync12(current, {
69315
+ mkdirSync13(current, {
68299
69316
  mode: 448
68300
69317
  });
68301
69318
  createdDirectories.push(current);
68302
69319
  }
68303
- const stat = lstatSync12(current);
69320
+ const stat = lstatSync13(current);
68304
69321
  if (!stat.isDirectory() || stat.isSymbolicLink())
68305
69322
  throw new Error(`benchmark staging parent must be a real directory: ${current}`);
68306
69323
  }
68307
69324
  }
68308
69325
  function pathExists(path) {
68309
69326
  try {
68310
- lstatSync12(path);
69327
+ lstatSync13(path);
68311
69328
  return true;
68312
69329
  } catch (error) {
68313
69330
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
@@ -68357,7 +69374,7 @@ class BenchmarkHostBackend {
68357
69374
  hostPath(path) {
68358
69375
  if (path === "/workspace")
68359
69376
  return this.workspace;
68360
- return join35(this.workspace, path.slice("/workspace/".length));
69377
+ return join37(this.workspace, path.slice("/workspace/".length));
68361
69378
  }
68362
69379
  }
68363
69380
  async function freezeRun(manifest, session, tools, faraiRoot, provider, kaliImageId) {
@@ -68505,7 +69522,7 @@ async function validateCandidates(manifest, sources, workspace) {
68505
69522
  }
68506
69523
  async function runOracle(oracle, candidate2, workspace, challengeId) {
68507
69524
  if (oracle.executableSha256) {
68508
- if (!existsSync26(oracle.command[0]))
69525
+ if (!existsSync29(oracle.command[0]))
68509
69526
  return {
68510
69527
  ok: false,
68511
69528
  error: "oracle executable missing"
@@ -68602,7 +69619,7 @@ var init_runner = __esm(() => {
68602
69619
  init_docker_lifecycle();
68603
69620
  init_hash();
68604
69621
  init_git_state();
68605
- init_manifest();
69622
+ init_manifest2();
68606
69623
  });
68607
69624
 
68608
69625
  // src/agent-benchmark/suite.ts
@@ -68613,16 +69630,16 @@ __export(exports_suite, {
68613
69630
  normalizeBenchmarkSuiteManifest: () => normalizeBenchmarkSuiteManifest,
68614
69631
  loadBenchmarkSuiteManifest: () => loadBenchmarkSuiteManifest
68615
69632
  });
68616
- import { mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4 } from "fs";
69633
+ import { mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync4 } from "fs";
68617
69634
  import { tmpdir as tmpdir5 } from "os";
68618
- import { join as join36 } from "path";
69635
+ import { join as join38 } from "path";
68619
69636
  async function runBenchmarkSuite(input, options = {}) {
68620
69637
  const manifest = normalizeBenchmarkSuiteManifest(input);
68621
69638
  const campaignId = id();
68622
- const root = options.artifactsDir ?? mkdtempSync4(join36(tmpdir5(), "farai-benchmark-campaign-"));
68623
- const bundlePath = join36(root, `${safeName3(manifest.id)}-${campaignId}`);
68624
- const runsPath = join36(bundlePath, "runs");
68625
- mkdirSync13(runsPath, {
69639
+ const root = options.artifactsDir ?? mkdtempSync4(join38(tmpdir5(), "farai-benchmark-campaign-"));
69640
+ const bundlePath = join38(root, `${safeName3(manifest.id)}-${campaignId}`);
69641
+ const runsPath = join38(bundlePath, "runs");
69642
+ mkdirSync14(runsPath, {
68626
69643
  recursive: true
68627
69644
  });
68628
69645
  const attempts = [];
@@ -68715,9 +69732,9 @@ async function runBenchmarkSuite(input, options = {}) {
68715
69732
  error: outcome.error
68716
69733
  })
68717
69734
  };
68718
- atomicWriteFile(join36(bundlePath, "campaign.json"), `${JSON.stringify(result, null, 2)}
69735
+ atomicWriteFile(join38(bundlePath, "campaign.json"), `${JSON.stringify(result, null, 2)}
68719
69736
  `, 384);
68720
- atomicWriteFile(join36(bundlePath, "suite.sha256"), `${result.manifestHash}
69737
+ atomicWriteFile(join38(bundlePath, "suite.sha256"), `${result.manifestHash}
68721
69738
  `, 384);
68722
69739
  return result;
68723
69740
  }
@@ -68758,7 +69775,7 @@ function safeName3(value) {
68758
69775
  var init_suite = __esm(() => {
68759
69776
  init_atomic_file();
68760
69777
  init_hash();
68761
- init_manifest();
69778
+ init_manifest2();
68762
69779
  init_runner();
68763
69780
  });
68764
69781
 
@@ -68773,6 +69790,7 @@ init_global_config();
68773
69790
  init_config();
68774
69791
  init_branding();
68775
69792
  init_version();
69793
+ init_session_catalog();
68776
69794
 
68777
69795
  // src/cli/command-arguments.ts
68778
69796
  init_model_provider_validation();
@@ -68781,6 +69799,16 @@ function parseNoArguments(command, args) {
68781
69799
  if (args.length > 0)
68782
69800
  throw new Error(`${command} does not accept arguments`);
68783
69801
  }
69802
+ function parseUpdateArguments(args) {
69803
+ const subcommand = args[0] ?? "status";
69804
+ if (subcommand !== "check" && subcommand !== "apply" && subcommand !== "status" && subcommand !== "rollback") {
69805
+ throw new Error(`unknown update command: ${subcommand}`);
69806
+ }
69807
+ parseNoArguments(`update ${subcommand}`, args.slice(1));
69808
+ return {
69809
+ kind: subcommand
69810
+ };
69811
+ }
68784
69812
  function parseSetupArguments(args) {
68785
69813
  const {
68786
69814
  values,
@@ -69280,6 +70308,13 @@ async function main() {
69280
70308
  ensureDefaultUserConfig();
69281
70309
  console.log(globalConfigPath());
69282
70310
  break;
70311
+ case "update":
70312
+ if (wantsHelp(args2)) {
70313
+ help("update");
70314
+ break;
70315
+ }
70316
+ await updateContent(args2);
70317
+ break;
69283
70318
  default:
69284
70319
  console.error(`unknown command: ${command}`);
69285
70320
  help();
@@ -69311,6 +70346,11 @@ async function doctor() {
69311
70346
  console.log(`kali image: ${backend2.image} (${image.exists ? "exists" : "missing"})`);
69312
70347
  console.log(`kali contract: ${image.contract ?? "missing"}`);
69313
70348
  console.log(`kali capabilities: ${image.contract === KALI_IMAGE_CONTRACT ? "ready" : "rebuild required"}`);
70349
+ const {
70350
+ contentStatus: contentStatus2
70351
+ } = await Promise.resolve().then(() => (init_updater(), exports_updater));
70352
+ const content = contentStatus2();
70353
+ console.log(`content: ${content.active?.version ?? "local fallback"}`);
69314
70354
  console.log(`setup command: farai setup`);
69315
70355
  }
69316
70356
  async function setup(args2) {
@@ -69451,6 +70491,14 @@ async function initLab(args2) {
69451
70491
  }
69452
70492
  async function launchTui(workspace, sessionId) {
69453
70493
  ensureDefaultUserConfig();
70494
+ const {
70495
+ runStartupContentPreflight: runStartupContentPreflight2
70496
+ } = await Promise.resolve().then(() => (init_preflight(), exports_preflight));
70497
+ const effectiveWorkspace = sessionId ? resolveSessionLocation(sessionId)?.workspace ?? workspace : workspace;
70498
+ if (await runStartupContentPreflight2(effectiveWorkspace) === "cancelled") {
70499
+ process.exitCode = 130;
70500
+ return;
70501
+ }
69454
70502
  if (import.meta.path.endsWith(".ts")) {
69455
70503
  const sourceTuiPreload = "@opentui/solid/preload";
69456
70504
  await import(sourceTuiPreload);
@@ -69468,6 +70516,13 @@ async function launchTui(workspace, sessionId) {
69468
70516
  process.exitCode = 1;
69469
70517
  }
69470
70518
  }
70519
+ async function updateContent(args2) {
70520
+ const parsed = parseUpdateArguments(args2);
70521
+ const {
70522
+ runContentUpdateCommand: runContentUpdateCommand2
70523
+ } = await Promise.resolve().then(() => (init_command2(), exports_command2));
70524
+ process.exitCode = await runContentUpdateCommand2(parsed, process.cwd());
70525
+ }
69471
70526
  async function run2(args2) {
69472
70527
  const {
69473
70528
  sessionId,
@@ -69633,7 +70688,14 @@ Usage:
69633
70688
  config: `Farai config
69634
70689
 
69635
70690
  Usage:
69636
- farai config`
70691
+ farai config`,
70692
+ update: `Farai update
70693
+
70694
+ Usage:
70695
+ farai update status
70696
+ farai update check
70697
+ farai update apply
70698
+ farai update rollback`
69637
70699
  };
69638
70700
  if (topic && !pages[topic])
69639
70701
  throw new Error(`unknown help topic: ${topic}`);
@@ -69651,6 +70713,7 @@ Usage:
69651
70713
  farai bench run <manifest.json> [--output result.json] [--workspace scratch-dir] [--artifacts dir]
69652
70714
  farai bench suite <suite.json> [--artifacts dir]
69653
70715
  farai config
70716
+ farai update [status|check|apply|rollback]
69654
70717
 
69655
70718
  settings live in ~/.local/pajarori/farai/config.toml; credentials use the system keyring.
69656
70719
 
@@ -69664,5 +70727,5 @@ Examples:
69664
70727
  `);
69665
70728
  }
69666
70729
 
69667
- //# debugId=C611ADC20CC5D27764756E2164756E21
70730
+ //# debugId=59D0DF40CE3D0BDB64756E2164756E21
69668
70731
  //# sourceMappingURL=index.js.map