farai 0.2.6 → 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.6")
7423
- return "0.2.6";
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);
@@ -26107,6 +26260,9 @@ function emailMessageResult(action, emailId, address, message) {
26107
26260
  }
26108
26261
  };
26109
26262
  }
26263
+ function formatWaitCriteria(match) {
26264
+ return [match.from ? `sender containing ${JSON.stringify(match.from)}` : undefined, match.subject ? `subject containing ${JSON.stringify(match.subject)}` : undefined, match.body ? `body containing ${JSON.stringify(match.body)}` : undefined].filter((value) => value !== undefined).join(" and ");
26265
+ }
26110
26266
  function formatResource(resource) {
26111
26267
  return [`${resource.label} \xB7 ${resource.address}`, `id: ${resource.id}`, [resource.type, resource.provider, resource.status, ...resource.roles].join(" \xB7 ")].join(`
26112
26268
  `);
@@ -26323,7 +26479,7 @@ var init_email = __esm(() => {
26323
26479
  };
26324
26480
  emailWaitTool = {
26325
26481
  name: "email_wait",
26326
- description: "Wait for a matching message in one email resource using its exact Farai UUID. Use this for verification links, OTP codes, password resets, and asynchronous registrations. Match by sender, subject, or body text. The returned message UUID can be passed directly to email_read.",
26482
+ description: "Wait for a matching message in one email resource using its exact Farai UUID. Use this for verification links, OTP codes, password resets, and asynchronous registrations. Every supplied from, subject, and body filter is a strict case-insensitive substring condition and all supplied filters must match. Do not guess a subject or sender; omit filters when any message in an isolated temporary inbox is acceptable. The returned message UUID can be passed directly to email_read.",
26327
26483
  inputSchema: {
26328
26484
  type: "object",
26329
26485
  required: ["emailId"],
@@ -26334,15 +26490,15 @@ var init_email = __esm(() => {
26334
26490
  },
26335
26491
  from: {
26336
26492
  type: "string",
26337
- description: "Case-insensitive sender substring"
26493
+ description: "Strict case-insensitive sender substring; omit when the sender is not known"
26338
26494
  },
26339
26495
  subject: {
26340
26496
  type: "string",
26341
- description: "Case-insensitive subject substring"
26497
+ description: "Strict case-insensitive subject substring; omit rather than guessing the subject"
26342
26498
  },
26343
26499
  body: {
26344
26500
  type: "string",
26345
- description: "Case-insensitive body substring"
26501
+ description: "Strict case-insensitive message-body substring; use only when the expected body text is known"
26346
26502
  },
26347
26503
  unreadOnly: {
26348
26504
  type: "boolean",
@@ -26390,16 +26546,20 @@ var init_email = __esm(() => {
26390
26546
  } : {}
26391
26547
  }, context.signal);
26392
26548
  if (!providerMessage) {
26549
+ const criteria = formatWaitCriteria(match);
26393
26550
  return {
26394
26551
  ok: true,
26395
26552
  summary: `no matching email arrived in ${source.address} within ${Math.round(timeoutMs / 1000)} seconds`,
26396
- output: "no matching message arrived before the timeout",
26553
+ output: criteria ? `no message matched ${criteria} before the timeout` : "no email arrived before the timeout",
26397
26554
  metadata: {
26398
26555
  emailAction: "wait",
26399
26556
  emailId,
26400
26557
  address: source.address,
26401
26558
  source: source.kind,
26402
- timedOut: true
26559
+ timedOut: true,
26560
+ ...criteria ? {
26561
+ criteria
26562
+ } : {}
26403
26563
  }
26404
26564
  };
26405
26565
  }
@@ -27534,8 +27694,8 @@ var init_model_profiles = __esm(() => {
27534
27694
  });
27535
27695
 
27536
27696
  // src/agent-core/model-catalog.ts
27537
- import { existsSync as existsSync12 } from "fs";
27538
- 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";
27539
27699
  async function buildModelCatalog(workspace, profiles = loadModelProfiles(workspace)) {
27540
27700
  const providers = [];
27541
27701
  const models = [];
@@ -28111,7 +28271,7 @@ async function fetchModelsDevCatalog(fetchImpl = globalThis.fetch, timeoutMs = M
28111
28271
  function readCachedModelsDevCatalog() {
28112
28272
  try {
28113
28273
  const path = modelsDevCachePath();
28114
- if (!existsSync12(path))
28274
+ if (!existsSync13(path))
28115
28275
  return;
28116
28276
  ensurePrivateDirectory(dirname7(path), "model cache directory");
28117
28277
  ensurePrivateRegularFileIfExists(path, "models.dev cache");
@@ -28163,7 +28323,7 @@ function ensureConcrete(resolved) {
28163
28323
  return resolved;
28164
28324
  }
28165
28325
  function modelsDevCachePath() {
28166
- return join17(globalDataDir(), "cache", "models-dev.json");
28326
+ return join18(globalDataDir(), "cache", "models-dev.json");
28167
28327
  }
28168
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;
28169
28329
  var init_model_catalog = __esm(() => {
@@ -30498,17 +30658,17 @@ var init_compaction = __esm(() => {
30498
30658
  });
30499
30659
 
30500
30660
  // src/agent-core/hooks/host.ts
30501
- import { existsSync as existsSync13 } from "fs";
30502
- import { join as join18 } from "path";
30661
+ import { existsSync as existsSync14 } from "fs";
30662
+ import { join as join19 } from "path";
30503
30663
  function hookConfigPaths(workspace) {
30504
30664
  if (false)
30505
30665
  ;
30506
- return [join18(localFaraiDir(), "hooks.json"), join18(workspace, ".farai", "hooks.json")];
30666
+ return [join19(localFaraiDir(), "hooks.json"), join19(workspace, ".farai", "hooks.json")];
30507
30667
  }
30508
30668
  function loadHooks(workspace) {
30509
30669
  const hooks = [];
30510
30670
  for (const path of hookConfigPaths(workspace)) {
30511
- if (!existsSync13(path))
30671
+ if (!existsSync14(path))
30512
30672
  continue;
30513
30673
  try {
30514
30674
  const parsed = JSON.parse(readBoundedFileTextSync(path, HOOK_CONFIG_MAX_BYTES, "hook config"));
@@ -30632,7 +30792,7 @@ class SubagentGate {
30632
30792
  idle() {
30633
30793
  if (this.active === 0 && this.queue.length === 0)
30634
30794
  return Promise.resolve();
30635
- return new Promise((resolve8) => this.idleResolvers.add(resolve8));
30795
+ return new Promise((resolve9) => this.idleResolvers.add(resolve9));
30636
30796
  }
30637
30797
  async run(work, signal) {
30638
30798
  const release = await this.acquire(signal);
@@ -30644,13 +30804,13 @@ class SubagentGate {
30644
30804
  }
30645
30805
  }
30646
30806
  acquire(signal) {
30647
- return new Promise((resolve8, reject) => {
30807
+ return new Promise((resolve9, reject) => {
30648
30808
  if (signal?.aborted) {
30649
30809
  reject(signal.reason ?? new Error("subagent task cancelled before start"));
30650
30810
  return;
30651
30811
  }
30652
30812
  const waiter = {
30653
- resolve: resolve8,
30813
+ resolve: resolve9,
30654
30814
  reject,
30655
30815
  ...signal ? {
30656
30816
  signal
@@ -30697,8 +30857,8 @@ class SubagentGate {
30697
30857
  notifyIdle() {
30698
30858
  if (this.active !== 0 || this.queue.length !== 0)
30699
30859
  return;
30700
- for (const resolve8 of this.idleResolvers)
30701
- resolve8();
30860
+ for (const resolve9 of this.idleResolvers)
30861
+ resolve9();
30702
30862
  this.idleResolvers.clear();
30703
30863
  }
30704
30864
  }
@@ -30712,10 +30872,10 @@ class SessionActor {
30712
30872
  run(work) {
30713
30873
  if (this.closed)
30714
30874
  return Promise.reject(new Error("Session actor is closed"));
30715
- return new Promise((resolve8, reject) => {
30875
+ return new Promise((resolve9, reject) => {
30716
30876
  this.queue.push({
30717
30877
  work,
30718
- resolve: (value) => resolve8(value),
30878
+ resolve: (value) => resolve9(value),
30719
30879
  reject
30720
30880
  });
30721
30881
  this.drain();
@@ -30727,7 +30887,7 @@ class SessionActor {
30727
30887
  idle() {
30728
30888
  if (!this.running && this.queue.length === 0)
30729
30889
  return Promise.resolve();
30730
- return new Promise((resolve8) => this.idleResolvers.add(resolve8));
30890
+ return new Promise((resolve9) => this.idleResolvers.add(resolve9));
30731
30891
  }
30732
30892
  close() {
30733
30893
  if (this.closed)
@@ -30760,8 +30920,8 @@ class SessionActor {
30760
30920
  resolveIdle() {
30761
30921
  if (this.running || this.queue.length > 0)
30762
30922
  return;
30763
- for (const resolve8 of this.idleResolvers)
30764
- resolve8();
30923
+ for (const resolve9 of this.idleResolvers)
30924
+ resolve9();
30765
30925
  this.idleResolvers.clear();
30766
30926
  }
30767
30927
  }
@@ -32875,7 +33035,7 @@ function rebuildFtsIndexes(db) {
32875
33035
  var KNOWLEDGE_SCHEMA_VERSION = 2;
32876
33036
 
32877
33037
  // src/agent-knowledge/store.ts
32878
- import { existsSync as existsSync14 } from "fs";
33038
+ import { existsSync as existsSync15 } from "fs";
32879
33039
  import { Database as Database3 } from "bun:sqlite";
32880
33040
 
32881
33041
  class KnowledgeStore {
@@ -32884,7 +33044,7 @@ class KnowledgeStore {
32884
33044
  this.create = create;
32885
33045
  }
32886
33046
  static openIfExists(path) {
32887
- if (!existsSync14(path))
33047
+ if (!existsSync15(path))
32888
33048
  return;
32889
33049
  const store = new KnowledgeStore(path);
32890
33050
  try {
@@ -33459,24 +33619,28 @@ var init_store = __esm(() => {
33459
33619
  });
33460
33620
 
33461
33621
  // src/agent-knowledge/paths.ts
33462
- import { join as join19 } from "path";
33622
+ import { join as join20 } from "path";
33463
33623
  function knowledgeDbPath() {
33464
- return join19(localFaraiDir(), "knowledge.db");
33624
+ return activeContentKnowledgePath() ?? legacyKnowledgeDbPath();
33625
+ }
33626
+ function legacyKnowledgeDbPath() {
33627
+ return join20(localFaraiDir(), "knowledge.db");
33465
33628
  }
33466
33629
  function knowledgeRoot() {
33467
- return process.env.FARAI_KNOWLEDGE_DIR ?? join19(localFaraiDir(), "knowledge");
33630
+ return process.env.FARAI_KNOWLEDGE_DIR ?? join20(localFaraiDir(), "knowledge");
33468
33631
  }
33469
33632
  function packsDir() {
33470
- return join19(knowledgeRoot(), "packs");
33633
+ return join20(knowledgeRoot(), "packs");
33471
33634
  }
33472
33635
  function taxonomyDir() {
33473
- return join19(knowledgeRoot(), "taxonomy");
33636
+ return join20(knowledgeRoot(), "taxonomy");
33474
33637
  }
33475
33638
  function cacheDir() {
33476
- return join19(localFaraiDir(), "knowledge-cache");
33639
+ return join20(localFaraiDir(), "knowledge-cache");
33477
33640
  }
33478
- var init_paths2 = __esm(() => {
33641
+ var init_paths3 = __esm(() => {
33479
33642
  init_config();
33643
+ init_paths2();
33480
33644
  });
33481
33645
 
33482
33646
  // src/agent-core/model-pricing.ts
@@ -33510,14 +33674,14 @@ function nonNegative(value) {
33510
33674
  }
33511
33675
 
33512
33676
  // src/session-catalog.ts
33513
- import { existsSync as existsSync15, readdirSync as readdirSync5, rmSync as rmSync2 } from "fs";
33514
- 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";
33515
33679
  function recordSessionLocation(session) {
33516
33680
  const directory = catalogDirectory();
33517
33681
  ensurePrivateDirectory(directory, "session catalog directory");
33518
33682
  const entry = {
33519
33683
  id: session.id,
33520
- workspace: resolve8(session.workspace),
33684
+ workspace: resolve9(session.workspace),
33521
33685
  ...session.title ? {
33522
33686
  title: session.title
33523
33687
  } : {},
@@ -33526,13 +33690,13 @@ function recordSessionLocation(session) {
33526
33690
  } : {},
33527
33691
  updatedAt: session.updatedAt
33528
33692
  };
33529
- const path = join20(directory, `${session.id}.json`);
33693
+ const path = join21(directory, `${session.id}.json`);
33530
33694
  ensurePrivateRegularFileIfExists(path, "session catalog entry");
33531
33695
  atomicWriteFile(path, `${JSON.stringify(entry)}
33532
33696
  `, 384);
33533
33697
  }
33534
33698
  function removeSessionLocation(sessionId) {
33535
- rmSync2(join20(catalogDirectory(), `${sessionId}.json`), {
33699
+ rmSync2(join21(catalogDirectory(), `${sessionId}.json`), {
33536
33700
  force: true
33537
33701
  });
33538
33702
  }
@@ -33552,7 +33716,7 @@ function resolveSessionLocation(query) {
33552
33716
  }
33553
33717
  function listSessionLocations() {
33554
33718
  const directory = catalogDirectory();
33555
- if (!existsSync15(directory))
33719
+ if (!existsSync16(directory))
33556
33720
  return [];
33557
33721
  try {
33558
33722
  ensurePrivateDirectory(directory, "session catalog directory");
@@ -33561,7 +33725,7 @@ function listSessionLocations() {
33561
33725
  }
33562
33726
  return readdirSync5(directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
33563
33727
  try {
33564
- const path = join20(directory, name);
33728
+ const path = join21(directory, name);
33565
33729
  ensurePrivateRegularFileIfExists(path, "session catalog entry");
33566
33730
  const value = JSON.parse(readBoundedFileTextSyncNoFollow(path, SESSION_CATALOG_ENTRY_MAX_BYTES, "session catalog entry"));
33567
33731
  if (typeof value.id !== "string" || typeof value.workspace !== "string" || typeof value.updatedAt !== "string")
@@ -33583,10 +33747,10 @@ function listSessionLocations() {
33583
33747
  }).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
33584
33748
  }
33585
33749
  function catalogDirectory() {
33586
- return join20(localFaraiDir(), "sessions");
33750
+ return join21(localFaraiDir(), "sessions");
33587
33751
  }
33588
33752
  function sessionDatabaseExists(workspace) {
33589
- return existsSync15(join20(workspace, ".farai", "farai.db"));
33753
+ return existsSync16(join21(workspace, ".farai", "farai.db"));
33590
33754
  }
33591
33755
  var SESSION_CATALOG_ENTRY_MAX_BYTES;
33592
33756
  var init_session_catalog = __esm(() => {
@@ -33622,7 +33786,7 @@ class SessionUserInputCoordinator {
33622
33786
  expiresAt
33623
33787
  } : {}
33624
33788
  };
33625
- return new Promise((resolve9, reject) => {
33789
+ return new Promise((resolve10, reject) => {
33626
33790
  const detach = () => signal?.removeEventListener("abort", abort);
33627
33791
  const abort = () => {
33628
33792
  if (this.pending.get(sessionId)?.request.id !== request.id)
@@ -33639,7 +33803,7 @@ class SessionUserInputCoordinator {
33639
33803
  request,
33640
33804
  resolve: (answer) => {
33641
33805
  detach();
33642
- resolve9(answer);
33806
+ resolve10(answer);
33643
33807
  },
33644
33808
  reject: (error) => {
33645
33809
  detach();
@@ -33972,7 +34136,7 @@ class ToolExecutionGate {
33972
34136
  idle() {
33973
34137
  if (this.states.size === 0)
33974
34138
  return Promise.resolve();
33975
- return new Promise((resolve9) => this.idleResolvers.add(resolve9));
34139
+ return new Promise((resolve10) => this.idleResolvers.add(resolve10));
33976
34140
  }
33977
34141
  async run(key, parallel, fn, signal) {
33978
34142
  const release = await this.acquire(key, parallel ? "read" : "write", signal);
@@ -33984,7 +34148,7 @@ class ToolExecutionGate {
33984
34148
  }
33985
34149
  }
33986
34150
  acquire(key, mode, signal) {
33987
- return new Promise((resolve9, reject) => {
34151
+ return new Promise((resolve10, reject) => {
33988
34152
  if (signal?.aborted) {
33989
34153
  reject(signal.reason ?? new Error("tool gate acquisition cancelled"));
33990
34154
  return;
@@ -33997,7 +34161,7 @@ class ToolExecutionGate {
33997
34161
  this.states.set(key, state);
33998
34162
  const waiter = {
33999
34163
  mode,
34000
- resolve: resolve9,
34164
+ resolve: resolve10,
34001
34165
  reject,
34002
34166
  ...signal ? {
34003
34167
  signal
@@ -34054,8 +34218,8 @@ class ToolExecutionGate {
34054
34218
  if (state.activeReaders === 0 && !state.activeWriter && state.queue.length === 0 && this.states.get(key) === state) {
34055
34219
  this.states.delete(key);
34056
34220
  if (this.states.size === 0) {
34057
- for (const resolve9 of this.idleResolvers)
34058
- resolve9();
34221
+ for (const resolve10 of this.idleResolvers)
34222
+ resolve10();
34059
34223
  this.idleResolvers.clear();
34060
34224
  }
34061
34225
  }
@@ -34102,7 +34266,7 @@ function abortablePromise(promise, signal) {
34102
34266
  return promise;
34103
34267
  if (signal.aborted)
34104
34268
  return Promise.reject(abortReason(signal));
34105
- return new Promise((resolve9, reject) => {
34269
+ return new Promise((resolve10, reject) => {
34106
34270
  const cleanup = () => signal.removeEventListener("abort", onAbort);
34107
34271
  const onAbort = () => {
34108
34272
  cleanup();
@@ -34113,7 +34277,7 @@ function abortablePromise(promise, signal) {
34113
34277
  });
34114
34278
  promise.then((value) => {
34115
34279
  cleanup();
34116
- resolve9(value);
34280
+ resolve10(value);
34117
34281
  }, (error) => {
34118
34282
  cleanup();
34119
34283
  reject(error);
@@ -34498,8 +34662,8 @@ var init_tool_call_journal = () => {};
34498
34662
 
34499
34663
  // src/agent-core/runtime.ts
34500
34664
  import { createHash as createHash9 } from "crypto";
34501
- import { existsSync as existsSync16, mkdirSync as mkdirSync6, realpathSync as realpathSync3 } from "fs";
34502
- 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";
34503
34667
  function assertProviderToolIndex2(index, max) {
34504
34668
  if (!Number.isInteger(index) || index < 0 || index >= max)
34505
34669
  throw new Error(`provider tool call index must be between 0 and ${max - 1}`);
@@ -34545,7 +34709,7 @@ class AgentRuntime {
34545
34709
  this.workspace = workspace;
34546
34710
  this.inheritConfig = options.inheritConfig !== false;
34547
34711
  const config = this.inheritConfig ? loadConfig(workspace) : {};
34548
- this.store = new SqliteStore(join21(workspace, ".farai"));
34712
+ this.store = new SqliteStore(join22(workspace, ".farai"));
34549
34713
  this.toolCalls = new ToolCallJournal(this.store, (sessionId, type, payload) => this.event(sessionId, type, payload));
34550
34714
  this.knowledgeEnabled = options.enableKnowledge !== false;
34551
34715
  this.hooksEnabled = options.enableHooks !== false;
@@ -35367,16 +35531,16 @@ class AgentRuntime {
35367
35531
  }
35368
35532
  waitForSteering(sessionId) {
35369
35533
  let settled = false;
35370
- let resolve9;
35534
+ let resolve10;
35371
35535
  const promise = new Promise((done) => {
35372
- resolve9 = done;
35536
+ resolve10 = done;
35373
35537
  });
35374
35538
  const wake = () => {
35375
35539
  if (settled)
35376
35540
  return;
35377
35541
  settled = true;
35378
35542
  this.steeringWaiters.get(sessionId)?.delete(wake);
35379
- resolve9();
35543
+ resolve10();
35380
35544
  };
35381
35545
  const waiters = this.steeringWaiters.get(sessionId) ?? new Set;
35382
35546
  waiters.add(wake);
@@ -35724,12 +35888,12 @@ class AgentRuntime {
35724
35888
  return {
35725
35889
  markdown
35726
35890
  };
35727
- const dir = join21(this.workspace, ".farai", "reports");
35891
+ const dir = join22(this.workspace, ".farai", "reports");
35728
35892
  mkdirSync6(dir, {
35729
35893
  recursive: true
35730
35894
  });
35731
35895
  const stamp = new Date().toISOString().slice(0, 10);
35732
- const path = join21(dir, `${sessionId}-${stamp}.md`);
35896
+ const path = join22(dir, `${sessionId}-${stamp}.md`);
35733
35897
  atomicWriteFile(path, markdown, 384);
35734
35898
  return {
35735
35899
  markdown,
@@ -37829,7 +37993,7 @@ This completion is already terminal and was delivered automatically. Do not call
37829
37993
  }
37830
37994
  if (entries.length === 0 || entries.some((entry) => !entry.running) || Date.now() - started >= timeoutMs)
37831
37995
  return entries;
37832
- await new Promise((resolve9) => setTimeout(resolve9, 200));
37996
+ await new Promise((resolve10) => setTimeout(resolve10, 200));
37833
37997
  }
37834
37998
  },
37835
37999
  message: (childSessionId, text2) => {
@@ -37889,15 +38053,15 @@ This completion is already terminal and was delivered automatically. Do not call
37889
38053
  const root = (await runHostGit(this.workspace, ["rev-parse", "--show-toplevel"])).trim();
37890
38054
  if (realpathSync3(root) !== realpathSync3(this.workspace))
37891
38055
  throw new Error(`Farai workspace is not the Git repository root: ${root}`);
37892
- const worktreesRoot = join21(this.workspace, ".farai", "worktrees");
38056
+ const worktreesRoot = join22(this.workspace, ".farai", "worktrees");
37893
38057
  mkdirSync6(worktreesRoot, {
37894
38058
  recursive: true
37895
38059
  });
37896
- const path = join21(worktreesRoot, safeName);
38060
+ const path = join22(worktreesRoot, safeName);
37897
38061
  const registered = await registeredWorktree(this.workspace, path);
37898
- if (existsSync16(path) && !registered)
38062
+ if (existsSync17(path) && !registered)
37899
38063
  throw new Error(`worktree path already exists but is not a registered Git worktree: ${path}`);
37900
- if (!existsSync16(path) && registered)
38064
+ if (!existsSync17(path) && registered)
37901
38065
  throw new Error(`worktree registration exists but its directory is missing: ${path}; repair or prune it before re-entry`);
37902
38066
  if (registered) {
37903
38067
  if (ref || branch)
@@ -37947,9 +38111,9 @@ This completion is already terminal and was delivered automatically. Do not call
37947
38111
  if (current.workspace === this.workspace)
37948
38112
  throw new Error("session is not inside an isolated worktree");
37949
38113
  this.assertWorkspaceTransitionIdle(session.id);
37950
- const worktreesRoot = join21(this.workspace, ".farai", "worktrees");
38114
+ const worktreesRoot = join22(this.workspace, ".farai", "worktrees");
37951
38115
  const managedPath = relative7(worktreesRoot, current.workspace);
37952
- if (!managedPath || managedPath.startsWith("..") || isAbsolute6(managedPath)) {
38116
+ if (!managedPath || managedPath.startsWith("..") || isAbsolute7(managedPath)) {
37953
38117
  throw new Error(`refusing to leave an unmanaged worktree: ${current.workspace}`);
37954
38118
  }
37955
38119
  if (remove) {
@@ -38831,16 +38995,16 @@ function plannerRetryState(error, attempt, safeToReplay) {
38831
38995
  function abortableSleep(ms, signal) {
38832
38996
  if (signal?.aborted)
38833
38997
  return Promise.resolve();
38834
- return new Promise((resolve9) => {
38998
+ return new Promise((resolve10) => {
38835
38999
  const cleanup = () => signal?.removeEventListener("abort", onAbort);
38836
39000
  const onAbort = () => {
38837
39001
  clearTimeout(timer);
38838
39002
  cleanup();
38839
- resolve9();
39003
+ resolve10();
38840
39004
  };
38841
39005
  const timer = setTimeout(() => {
38842
39006
  cleanup();
38843
- resolve9();
39007
+ resolve10();
38844
39008
  }, ms);
38845
39009
  signal?.addEventListener("abort", onAbort, {
38846
39010
  once: true
@@ -38890,18 +39054,18 @@ function shutdownGracePeriod(value) {
38890
39054
  return Math.max(0, value);
38891
39055
  }
38892
39056
  function waitForShutdownFinalization(finalization, gracePeriodMs) {
38893
- return new Promise((resolve9, reject) => {
39057
+ return new Promise((resolve10, reject) => {
38894
39058
  let settled = false;
38895
39059
  const timer = setTimeout(() => {
38896
39060
  settled = true;
38897
- resolve9(false);
39061
+ resolve10(false);
38898
39062
  }, gracePeriodMs);
38899
39063
  finalization.then(() => {
38900
39064
  if (settled)
38901
39065
  return;
38902
39066
  settled = true;
38903
39067
  clearTimeout(timer);
38904
- resolve9(true);
39068
+ resolve10(true);
38905
39069
  }, (error) => {
38906
39070
  if (settled)
38907
39071
  return;
@@ -39028,7 +39192,7 @@ var init_runtime = __esm(() => {
39028
39192
  init_retry();
39029
39193
  init_reasoning_summary();
39030
39194
  init_store();
39031
- init_paths2();
39195
+ init_paths3();
39032
39196
  init_session_catalog();
39033
39197
  init_session_user_input();
39034
39198
  init_tool_execution_control();
@@ -39079,37 +39243,796 @@ var init_branding = __esm(() => {
39079
39243
  `);
39080
39244
  });
39081
39245
 
39082
- // src/agent-knowledge/pack.ts
39083
- 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
+ });
39084
39419
  import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
39085
- 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";
39086
40009
  function packDir(meta) {
39087
40010
  assertPackPathPart(meta.id, "knowledge pack id");
39088
40011
  assertPackPathPart(meta.pin, "knowledge pack pin");
39089
- return join22(packsDir(), `${meta.id}@${meta.pin.slice(0, 12)}`);
40012
+ return join24(packsDir(), `${meta.id}@${meta.pin.slice(0, 12)}`);
39090
40013
  }
39091
40014
  function writePack(meta, records, entities) {
39092
40015
  const root = packsDir();
39093
40016
  ensurePrivateDirectory(root, "knowledge pack directory");
39094
40017
  const dir = packDir(meta);
39095
- const temporary = `${dir}.tmp-${process.pid}-${randomUUID4()}`;
39096
- mkdirSync7(temporary, {
40018
+ const temporary = `${dir}.tmp-${process.pid}-${randomUUID5()}`;
40019
+ mkdirSync8(temporary, {
39097
40020
  mode: 448
39098
40021
  });
39099
40022
  try {
39100
- atomicWriteFile(join22(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
40023
+ atomicWriteFile(join24(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
39101
40024
  `, 384);
39102
- writeJsonl(join22(temporary, "records.jsonl"), records, "knowledge records");
39103
- 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");
39104
40027
  if (realDirectoryExists(dir, "knowledge pack"))
39105
- rmSync3(dir, {
40028
+ rmSync4(dir, {
39106
40029
  recursive: true,
39107
40030
  force: true
39108
40031
  });
39109
- renameSync3(temporary, dir);
40032
+ renameSync4(temporary, dir);
39110
40033
  syncDirectory(root);
39111
40034
  } catch (error) {
39112
- rmSync3(temporary, {
40035
+ rmSync4(temporary, {
39113
40036
  recursive: true,
39114
40037
  force: true
39115
40038
  });
@@ -39119,19 +40042,19 @@ function writePack(meta, records, entities) {
39119
40042
  }
39120
40043
  function listPacks() {
39121
40044
  const root = packsDir();
39122
- if (!existsSync17(root))
40045
+ if (!existsSync19(root))
39123
40046
  return [];
39124
40047
  assertRealDirectory(root, "knowledge pack directory");
39125
- const entries = readdirSync6(root);
40048
+ const entries = readdirSync7(root);
39126
40049
  if (entries.length > PACK_DIRECTORY_MAX_COUNT)
39127
40050
  throw new Error(`knowledge pack directory exceeded ${PACK_DIRECTORY_MAX_COUNT} entries`);
39128
40051
  const out = [];
39129
40052
  for (const entry of entries) {
39130
- const dir = join22(root, entry);
40053
+ const dir = join24(root, entry);
39131
40054
  try {
39132
40055
  assertRealDirectory(dir, "knowledge pack");
39133
- const metaPath = join22(dir, "meta.json");
39134
- if (!existsSync17(metaPath))
40056
+ const metaPath = join24(dir, "meta.json");
40057
+ if (!existsSync19(metaPath))
39135
40058
  continue;
39136
40059
  const meta = JSON.parse(readBoundedFileTextSyncNoFollow(metaPath, PACK_META_MAX_BYTES, "knowledge pack metadata"));
39137
40060
  if (!validPackMeta(meta))
@@ -39155,14 +40078,14 @@ function latestPacks() {
39155
40078
  }
39156
40079
  function readRecords(dir) {
39157
40080
  assertRealDirectory(dir, "knowledge pack");
39158
- return readJsonl(join22(dir, "records.jsonl"), "knowledge records");
40081
+ return readJsonl(join24(dir, "records.jsonl"), "knowledge records");
39159
40082
  }
39160
40083
  function readEntities(dir) {
39161
40084
  assertRealDirectory(dir, "knowledge pack");
39162
- return readJsonl(join22(dir, "entities.jsonl"), "knowledge entities");
40085
+ return readJsonl(join24(dir, "entities.jsonl"), "knowledge entities");
39163
40086
  }
39164
40087
  function readJsonl(path, label) {
39165
- if (!existsSync17(path))
40088
+ if (!existsSync19(path))
39166
40089
  return [];
39167
40090
  const out = [];
39168
40091
  forEachFileLineSync(path, {
@@ -39183,7 +40106,7 @@ function readJsonl(path, label) {
39183
40106
  return out;
39184
40107
  }
39185
40108
  function writeJsonl(path, values, label) {
39186
- const descriptor = openSync4(path, "wx", 384);
40109
+ const descriptor = openSync5(path, "wx", 384);
39187
40110
  let totalBytes = 0;
39188
40111
  try {
39189
40112
  for (const value of values) {
@@ -39196,7 +40119,7 @@ function writeJsonl(path, values, label) {
39196
40119
  throw new Error(`${label} exceeded ${PACK_JSONL_MAX_BYTES} bytes`);
39197
40120
  let offset = 0;
39198
40121
  while (offset < line.byteLength) {
39199
- const written = writeSync(descriptor, line, offset, line.byteLength - offset);
40122
+ const written = writeSync2(descriptor, line, offset, line.byteLength - offset);
39200
40123
  if (written <= 0)
39201
40124
  throw new Error(`${label} write made no progress`);
39202
40125
  offset += written;
@@ -39204,7 +40127,7 @@ function writeJsonl(path, values, label) {
39204
40127
  }
39205
40128
  fsyncSync2(descriptor);
39206
40129
  } finally {
39207
- closeSync4(descriptor);
40130
+ closeSync5(descriptor);
39208
40131
  }
39209
40132
  }
39210
40133
  function validPackMeta(value) {
@@ -39215,21 +40138,21 @@ function assertPackPathPart(value, label) {
39215
40138
  throw new Error(`${label} is invalid`);
39216
40139
  }
39217
40140
  function realDirectoryExists(path, label) {
39218
- if (!existsSync17(path))
40141
+ if (!existsSync19(path))
39219
40142
  return false;
39220
40143
  assertRealDirectory(path, label);
39221
40144
  return true;
39222
40145
  }
39223
40146
  function assertRealDirectory(path, label) {
39224
- const stat = lstatSync4(path);
40147
+ const stat = lstatSync5(path);
39225
40148
  if (stat.isSymbolicLink() || !stat.isDirectory())
39226
40149
  throw new Error(`${label} must be a real directory`);
39227
40150
  }
39228
40151
  function recordId(pack, pin, discriminator) {
39229
- 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);
39230
40153
  }
39231
40154
  function sourceHash(body) {
39232
- return `sha256:${createHash10("sha256").update(body).digest("hex")}`;
40155
+ return `sha256:${createHash11("sha256").update(body).digest("hex")}`;
39233
40156
  }
39234
40157
  function extractEntities(recordId2, text2) {
39235
40158
  const found = new Set;
@@ -39258,7 +40181,7 @@ var init_pack = __esm(() => {
39258
40181
  init_atomic_file();
39259
40182
  init_private_path();
39260
40183
  init_file_read();
39261
- init_paths2();
40184
+ init_paths3();
39262
40185
  PACK_META_MAX_BYTES = 1024 * 1024;
39263
40186
  PACK_JSONL_MAX_BYTES = 256 * 1024 * 1024;
39264
40187
  PACK_LINE_MAX_BYTES = 64 * 1024 * 1024;
@@ -39282,34 +40205,34 @@ var init_pack = __esm(() => {
39282
40205
  });
39283
40206
 
39284
40207
  // src/agent-knowledge/ingest/taxonomy-pack.ts
39285
- 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";
39286
- import { randomUUID as randomUUID5 } from "crypto";
39287
- 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";
39288
40211
  function writeTaxonomy(meta, nodes, edges) {
39289
40212
  assertPathPart(meta.id, "taxonomy id");
39290
40213
  assertPathPart(meta.pin, "taxonomy pin");
39291
40214
  const root = taxonomyDir();
39292
40215
  ensurePrivateDirectory(root, "knowledge taxonomy directory");
39293
- const dir = join23(root, `${meta.id}@${meta.pin}`);
39294
- const temporary = `${dir}.tmp-${process.pid}-${randomUUID5()}`;
39295
- mkdirSync8(temporary, {
40216
+ const dir = join25(root, `${meta.id}@${meta.pin}`);
40217
+ const temporary = `${dir}.tmp-${process.pid}-${randomUUID6()}`;
40218
+ mkdirSync9(temporary, {
39296
40219
  recursive: true,
39297
40220
  mode: 448
39298
40221
  });
39299
40222
  try {
39300
- atomicWriteFile(join23(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
40223
+ atomicWriteFile(join25(temporary, "meta.json"), `${JSON.stringify(meta, null, 2)}
39301
40224
  `, 384);
39302
- writeJsonl2(join23(temporary, "nodes.jsonl"), nodes, "taxonomy nodes");
39303
- 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");
39304
40227
  if (realDirectoryExists2(dir, "knowledge taxonomy"))
39305
- rmSync4(dir, {
40228
+ rmSync5(dir, {
39306
40229
  recursive: true,
39307
40230
  force: true
39308
40231
  });
39309
- renameSync4(temporary, dir);
40232
+ renameSync5(temporary, dir);
39310
40233
  syncDirectory(root);
39311
40234
  } catch (error) {
39312
- rmSync4(temporary, {
40235
+ rmSync5(temporary, {
39313
40236
  recursive: true,
39314
40237
  force: true
39315
40238
  });
@@ -39318,7 +40241,7 @@ function writeTaxonomy(meta, nodes, edges) {
39318
40241
  return dir;
39319
40242
  }
39320
40243
  function writeJsonl2(path, values, label) {
39321
- const descriptor = openSync5(path, "wx", 384);
40244
+ const descriptor = openSync6(path, "wx", 384);
39322
40245
  let totalBytes = 0;
39323
40246
  try {
39324
40247
  for (const value of values) {
@@ -39331,7 +40254,7 @@ function writeJsonl2(path, values, label) {
39331
40254
  throw new Error(`${label} exceeded ${TAXONOMY_JSONL_MAX_BYTES} bytes`);
39332
40255
  let offset = 0;
39333
40256
  while (offset < line.byteLength) {
39334
- const written = writeSync2(descriptor, line, offset, line.byteLength - offset);
40257
+ const written = writeSync3(descriptor, line, offset, line.byteLength - offset);
39335
40258
  if (written <= 0)
39336
40259
  throw new Error(`${label} write made no progress`);
39337
40260
  offset += written;
@@ -39339,24 +40262,24 @@ function writeJsonl2(path, values, label) {
39339
40262
  }
39340
40263
  fsyncSync3(descriptor);
39341
40264
  } finally {
39342
- closeSync5(descriptor);
40265
+ closeSync6(descriptor);
39343
40266
  }
39344
40267
  }
39345
40268
  function listTaxonomies() {
39346
40269
  const root = taxonomyDir();
39347
- if (!existsSync18(root))
40270
+ if (!existsSync20(root))
39348
40271
  return [];
39349
40272
  assertRealDirectory2(root, "knowledge taxonomy directory");
39350
- const entries = readdirSync7(root);
40273
+ const entries = readdirSync8(root);
39351
40274
  if (entries.length > TAXONOMY_DIRECTORY_MAX_COUNT)
39352
40275
  throw new Error(`knowledge taxonomy directory exceeded ${TAXONOMY_DIRECTORY_MAX_COUNT} entries`);
39353
40276
  const out = [];
39354
40277
  for (const entry of entries) {
39355
- const dir = join23(root, entry);
40278
+ const dir = join25(root, entry);
39356
40279
  try {
39357
40280
  assertRealDirectory2(dir, "knowledge taxonomy");
39358
- const metaPath = join23(dir, "meta.json");
39359
- if (!existsSync18(metaPath))
40281
+ const metaPath = join25(dir, "meta.json");
40282
+ if (!existsSync20(metaPath))
39360
40283
  continue;
39361
40284
  const meta = JSON.parse(readBoundedFileTextSyncNoFollow(metaPath, TAXONOMY_META_MAX_BYTES, "taxonomy metadata"));
39362
40285
  if (typeof meta?.id !== "string" || typeof meta.pin !== "string" || typeof meta.retrievedAt !== "string")
@@ -39380,14 +40303,14 @@ function latestTaxonomies() {
39380
40303
  }
39381
40304
  function readNodes(dir) {
39382
40305
  assertRealDirectory2(dir, "knowledge taxonomy");
39383
- return readJsonl2(join23(dir, "nodes.jsonl"));
40306
+ return readJsonl2(join25(dir, "nodes.jsonl"));
39384
40307
  }
39385
40308
  function readEdges(dir) {
39386
40309
  assertRealDirectory2(dir, "knowledge taxonomy");
39387
- return readJsonl2(join23(dir, "edges.jsonl"));
40310
+ return readJsonl2(join25(dir, "edges.jsonl"));
39388
40311
  }
39389
40312
  function readJsonl2(path) {
39390
- if (!existsSync18(path))
40313
+ if (!existsSync20(path))
39391
40314
  return [];
39392
40315
  const out = [];
39393
40316
  forEachFileLineSync(path, {
@@ -39412,19 +40335,19 @@ function assertPathPart(value, label) {
39412
40335
  throw new Error(`${label} is invalid`);
39413
40336
  }
39414
40337
  function realDirectoryExists2(path, label) {
39415
- if (!existsSync18(path))
40338
+ if (!existsSync20(path))
39416
40339
  return false;
39417
40340
  assertRealDirectory2(path, label);
39418
40341
  return true;
39419
40342
  }
39420
40343
  function assertRealDirectory2(path, label) {
39421
- const stat = lstatSync5(path);
40344
+ const stat = lstatSync6(path);
39422
40345
  if (stat.isSymbolicLink() || !stat.isDirectory())
39423
40346
  throw new Error(`${label} must be a real directory`);
39424
40347
  }
39425
40348
  var TAXONOMY_META_MAX_BYTES, TAXONOMY_JSONL_MAX_BYTES, TAXONOMY_LINE_MAX_BYTES, TAXONOMY_ENTRY_MAX_COUNT = 1e6, TAXONOMY_DIRECTORY_MAX_COUNT = 4096;
39426
40349
  var init_taxonomy_pack = __esm(() => {
39427
- init_paths2();
40350
+ init_paths3();
39428
40351
  init_file_read();
39429
40352
  init_atomic_file();
39430
40353
  init_private_path();
@@ -39467,16 +40390,16 @@ var init_http2 = __esm(() => {
39467
40390
  });
39468
40391
 
39469
40392
  // src/agent-knowledge/ingest/enrichment.ts
39470
- import { createReadStream, existsSync as existsSync19 } from "fs";
40393
+ import { createReadStream, existsSync as existsSync21 } from "fs";
39471
40394
  import { open as open3, rename, unlink as unlink2 } from "fs/promises";
39472
- import { randomUUID as randomUUID6 } from "crypto";
40395
+ import { randomUUID as randomUUID7 } from "crypto";
39473
40396
  import { Writable } from "stream";
39474
40397
  import { pipeline } from "stream/promises";
39475
40398
  import { StringDecoder as StringDecoder3 } from "string_decoder";
39476
40399
  import { createGunzip } from "zlib";
39477
- import { join as join24 } from "path";
40400
+ import { join as join26 } from "path";
39478
40401
  function enrichmentDir() {
39479
- return join24(knowledgeRoot(), "enrichment");
40402
+ return join26(knowledgeRoot(), "enrichment");
39480
40403
  }
39481
40404
  async function ingestEnrichment() {
39482
40405
  const map = new Map;
@@ -39540,8 +40463,8 @@ async function ingestEnrichment() {
39540
40463
  };
39541
40464
  }
39542
40465
  function readEnrichment() {
39543
- const path = join24(enrichmentDir(), "enrichment.jsonl");
39544
- if (!existsSync19(path))
40466
+ const path = join26(enrichmentDir(), "enrichment.jsonl");
40467
+ if (!existsSync21(path))
39545
40468
  return [];
39546
40469
  const out = [];
39547
40470
  forEachFileLineSync(path, {
@@ -39562,7 +40485,7 @@ function readEnrichment() {
39562
40485
  return out;
39563
40486
  }
39564
40487
  async function forEachEpssLine(dir, consume) {
39565
- const archive = join24(dir, `.epss-${randomUUID6()}.csv.gz`);
40488
+ const archive = join26(dir, `.epss-${randomUUID7()}.csv.gz`);
39566
40489
  try {
39567
40490
  await downloadKnowledgeFile(EPSS_URL, archive, EPSS_GZIP_MAX_BYTES, "epss archive");
39568
40491
  await pipeline(createReadStream(archive), createGunzip(), new BoundedLineSink(EPSS_CSV_MAX_BYTES, EPSS_LINE_MAX_BYTES, consume));
@@ -39573,8 +40496,8 @@ async function forEachEpssLine(dir, consume) {
39573
40496
  }
39574
40497
  }
39575
40498
  async function writeEnrichmentRows(dir, rows) {
39576
- const output = join24(dir, "enrichment.jsonl");
39577
- const temporary = join24(dir, `.enrichment-${randomUUID6()}.jsonl`);
40499
+ const output = join26(dir, "enrichment.jsonl");
40500
+ const temporary = join26(dir, `.enrichment-${randomUUID7()}.jsonl`);
39578
40501
  let file;
39579
40502
  try {
39580
40503
  file = await open3(temporary, "wx", 384);
@@ -39635,7 +40558,7 @@ function recordValue2(value) {
39635
40558
  }
39636
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;
39637
40560
  var init_enrichment = __esm(() => {
39638
- init_paths2();
40561
+ init_paths3();
39639
40562
  init_http2();
39640
40563
  init_file_read();
39641
40564
  init_private_path();
@@ -39692,18 +40615,18 @@ var init_enrichment = __esm(() => {
39692
40615
  });
39693
40616
 
39694
40617
  // src/agent-knowledge/build.ts
39695
- import { createHash as createHash11, randomUUID as randomUUID7 } from "crypto";
39696
- import { mkdirSync as mkdirSync9, renameSync as renameSync5, rmSync as rmSync5 } from "fs";
39697
- 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";
39698
40621
  function buildKnowledgeDb(options = {}) {
39699
- const path = options.path ?? knowledgeDbPath();
39700
- const temporary = `${path}.tmp-${process.pid}-${randomUUID7()}`;
39701
- mkdirSync9(dirname9(path), {
40622
+ const path = options.path ?? legacyKnowledgeDbPath();
40623
+ const temporary = `${path}.tmp-${process.pid}-${randomUUID8()}`;
40624
+ mkdirSync10(dirname10(path), {
39702
40625
  recursive: true,
39703
40626
  mode: 448
39704
40627
  });
39705
40628
  if (options.path === undefined)
39706
- ensurePrivateDirectory(dirname9(path), "farai home directory");
40629
+ ensurePrivateDirectory(dirname10(path), "farai home directory");
39707
40630
  ensurePrivateRegularFileIfExists(path, "knowledge database");
39708
40631
  const store = new KnowledgeStore(temporary, true);
39709
40632
  const db = store.writable();
@@ -39760,18 +40683,18 @@ function buildKnowledgeDb(options = {}) {
39760
40683
  const actualEdges = rowCount(db, "kb_edges");
39761
40684
  store.close();
39762
40685
  ensurePrivateSqlitePath(temporary, "staged knowledge database");
39763
- rmSync5(`${temporary}-wal`, {
40686
+ rmSync6(`${temporary}-wal`, {
39764
40687
  force: true
39765
40688
  });
39766
- rmSync5(`${temporary}-shm`, {
40689
+ rmSync6(`${temporary}-shm`, {
39767
40690
  force: true
39768
40691
  });
39769
- rmSync5(`${temporary}-journal`, {
40692
+ rmSync6(`${temporary}-journal`, {
39770
40693
  force: true
39771
40694
  });
39772
- renameSync5(temporary, path);
40695
+ renameSync6(temporary, path);
39773
40696
  ensurePrivateRegularFileIfExists(path, "knowledge database");
39774
- syncDirectory(dirname9(path));
40697
+ syncDirectory(dirname10(path));
39775
40698
  return {
39776
40699
  path,
39777
40700
  packs: packs.length,
@@ -39786,16 +40709,16 @@ function buildKnowledgeDb(options = {}) {
39786
40709
  try {
39787
40710
  store.close();
39788
40711
  } catch {}
39789
- rmSync5(temporary, {
40712
+ rmSync6(temporary, {
39790
40713
  force: true
39791
40714
  });
39792
- rmSync5(`${temporary}-wal`, {
40715
+ rmSync6(`${temporary}-wal`, {
39793
40716
  force: true
39794
40717
  });
39795
- rmSync5(`${temporary}-shm`, {
40718
+ rmSync6(`${temporary}-shm`, {
39796
40719
  force: true
39797
40720
  });
39798
- rmSync5(`${temporary}-journal`, {
40721
+ rmSync6(`${temporary}-journal`, {
39799
40722
  force: true
39800
40723
  });
39801
40724
  throw error;
@@ -39819,7 +40742,7 @@ function trackDuplicate(groups, record3) {
39819
40742
  const normalized = record3.answer.replace(/\s+/g, " ").trim().toLowerCase();
39820
40743
  if (normalized.length < 64)
39821
40744
  return;
39822
- const key = createHash11("sha256").update(normalized).digest("hex");
40745
+ const key = createHash12("sha256").update(normalized).digest("hex");
39823
40746
  const list = groups.get(key) ?? [];
39824
40747
  list.push(record3.id);
39825
40748
  groups.set(key, list);
@@ -39842,24 +40765,24 @@ var init_build = __esm(() => {
39842
40765
  init_atomic_file();
39843
40766
  init_private_path();
39844
40767
  init_store();
39845
- init_paths2();
40768
+ init_paths3();
39846
40769
  init_pack();
39847
40770
  init_taxonomy_pack();
39848
40771
  init_enrichment();
39849
40772
  });
39850
40773
 
39851
40774
  // src/agent-knowledge/ingest/git-source.ts
39852
- import { existsSync as existsSync20, lstatSync as lstatSync6, renameSync as renameSync6, rmSync as rmSync6 } from "fs";
39853
- import { randomUUID as randomUUID8 } from "crypto";
39854
- 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";
39855
40778
  async function fetchGitSource(source) {
39856
40779
  assertSource(source);
39857
40780
  const root = cacheDir();
39858
40781
  ensurePrivateDirectory(root, "knowledge cache directory");
39859
- const dir = join25(root, source.id);
40782
+ const dir = join27(root, source.id);
39860
40783
  const cached = cachedRepositoryExists(dir);
39861
40784
  if (!cached) {
39862
- const staging = join25(root, `.${source.id}-clone-${randomUUID8()}`);
40785
+ const staging = join27(root, `.${source.id}-clone-${randomUUID9()}`);
39863
40786
  try {
39864
40787
  const args = ["clone", "--depth", "1", "--branch", source.branch];
39865
40788
  if (source.sparse?.length)
@@ -39868,9 +40791,9 @@ async function fetchGitSource(source) {
39868
40791
  await run("git", args);
39869
40792
  if (source.sparse?.length)
39870
40793
  await run("git", ["-C", staging, "sparse-checkout", "set", "--no-cone", "--", ...source.sparse]);
39871
- renameSync6(staging, dir);
40794
+ renameSync7(staging, dir);
39872
40795
  } finally {
39873
- rmSync6(staging, {
40796
+ rmSync7(staging, {
39874
40797
  recursive: true,
39875
40798
  force: true
39876
40799
  });
@@ -39938,22 +40861,22 @@ function assertSource(source) {
39938
40861
  throw new Error(`invalid sparse path for git source: ${source.id}`);
39939
40862
  }
39940
40863
  function cachedRepositoryExists(dir) {
39941
- if (!existsSync20(dir))
40864
+ if (!existsSync22(dir))
39942
40865
  return false;
39943
- const directory = lstatSync6(dir);
40866
+ const directory = lstatSync7(dir);
39944
40867
  if (directory.isSymbolicLink() || !directory.isDirectory())
39945
40868
  throw new Error(`git source cache must be a real directory: ${dir}`);
39946
- const git = join25(dir, ".git");
39947
- if (!existsSync20(git))
40869
+ const git = join27(dir, ".git");
40870
+ if (!existsSync22(git))
39948
40871
  throw new Error(`git source cache is not a repository: ${dir}`);
39949
- const metadata = lstatSync6(git);
40872
+ const metadata = lstatSync7(git);
39950
40873
  if (metadata.isSymbolicLink() || !metadata.isDirectory())
39951
40874
  throw new Error(`git source metadata must be a real directory: ${dir}`);
39952
40875
  return true;
39953
40876
  }
39954
40877
  var GIT_TIMEOUT_MS;
39955
40878
  var init_git_source = __esm(() => {
39956
- init_paths2();
40879
+ init_paths3();
39957
40880
  init_captured_process();
39958
40881
  init_output_buffer();
39959
40882
  init_private_path();
@@ -40065,11 +40988,11 @@ var init_markdown_chunk = __esm(() => {
40065
40988
  });
40066
40989
 
40067
40990
  // src/agent-knowledge/ingest/hacktricks.ts
40068
- import { existsSync as existsSync21, lstatSync as lstatSync7, readdirSync as readdirSync8 } from "fs";
40069
- 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";
40070
40993
  async function ingestHacktricks() {
40071
40994
  const fetched = await fetchGitSource(SOURCE);
40072
- const srcDir = join26(fetched.dir, "src");
40995
+ const srcDir = join28(fetched.dir, "src");
40073
40996
  const meta = {
40074
40997
  id: "hacktricks",
40075
40998
  sourceUrl: "https://github.com/HackTricks-wiki/hacktricks",
@@ -40121,9 +41044,9 @@ async function ingestHacktricks() {
40121
41044
  };
40122
41045
  }
40123
41046
  function markdownFiles(root) {
40124
- if (!existsSync21(root))
41047
+ if (!existsSync23(root))
40125
41048
  return [];
40126
- const rootInfo = lstatSync7(root);
41049
+ const rootInfo = lstatSync8(root);
40127
41050
  if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
40128
41051
  throw new Error("hacktricks source root must be a real directory");
40129
41052
  const out = [];
@@ -40131,12 +41054,12 @@ function markdownFiles(root) {
40131
41054
  let entries = 0;
40132
41055
  while (pending.length) {
40133
41056
  const dir = pending.pop();
40134
- for (const entry of readdirSync8(dir)) {
41057
+ for (const entry of readdirSync9(dir)) {
40135
41058
  entries += 1;
40136
41059
  if (entries > WALK_ENTRY_MAX_COUNT)
40137
41060
  throw new Error(`hacktricks source exceeded ${WALK_ENTRY_MAX_COUNT} entries`);
40138
- const full = join26(dir, entry);
40139
- const info = lstatSync7(full);
41061
+ const full = join28(dir, entry);
41062
+ const info = lstatSync8(full);
40140
41063
  if (info.isSymbolicLink())
40141
41064
  continue;
40142
41065
  if (info.isDirectory())
@@ -40166,8 +41089,8 @@ var init_hacktricks = __esm(() => {
40166
41089
  });
40167
41090
 
40168
41091
  // src/agent-knowledge/ingest/payloads.ts
40169
- import { existsSync as existsSync22, lstatSync as lstatSync8, readdirSync as readdirSync9 } from "fs";
40170
- 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";
40171
41094
  async function ingestPayloads() {
40172
41095
  const fetched = await fetchGitSource(SOURCE2);
40173
41096
  const meta = {
@@ -40223,9 +41146,9 @@ async function ingestPayloads() {
40223
41146
  };
40224
41147
  }
40225
41148
  function markdownFiles2(root) {
40226
- if (!existsSync22(root))
41149
+ if (!existsSync24(root))
40227
41150
  return [];
40228
- const rootInfo = lstatSync8(root);
41151
+ const rootInfo = lstatSync9(root);
40229
41152
  if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
40230
41153
  throw new Error("payload source root must be a real directory");
40231
41154
  const out = [];
@@ -40233,14 +41156,14 @@ function markdownFiles2(root) {
40233
41156
  let entries = 0;
40234
41157
  while (pending.length) {
40235
41158
  const dir = pending.pop();
40236
- for (const entry of readdirSync9(dir)) {
41159
+ for (const entry of readdirSync10(dir)) {
40237
41160
  if (entry === ".git")
40238
41161
  continue;
40239
41162
  entries += 1;
40240
41163
  if (entries > WALK_ENTRY_MAX_COUNT2)
40241
41164
  throw new Error(`payload source exceeded ${WALK_ENTRY_MAX_COUNT2} entries`);
40242
- const full = join27(dir, entry);
40243
- const info = lstatSync8(full);
41165
+ const full = join29(dir, entry);
41166
+ const info = lstatSync9(full);
40244
41167
  if (info.isSymbolicLink())
40245
41168
  continue;
40246
41169
  if (info.isDirectory())
@@ -40482,15 +41405,15 @@ var init_attack = __esm(() => {
40482
41405
  });
40483
41406
 
40484
41407
  // src/agent-knowledge/ingest/zip-fetch.ts
40485
- import { existsSync as existsSync23, lstatSync as lstatSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, renameSync as renameSync7, rmSync as rmSync7, unlinkSync as unlinkSync6 } from "fs";
40486
- import { randomUUID as randomUUID9 } from "crypto";
40487
- 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";
40488
41411
  async function fetchZippedXml(id2, url, pattern) {
40489
- const dir = join28(cacheDir(), id2);
41412
+ const dir = join30(cacheDir(), id2);
40490
41413
  ensurePrivateDirectory(dir, `${id2} knowledge cache directory`);
40491
- const token = randomUUID9();
40492
- const zipPath = join28(dir, `.download-${token}.zip`);
40493
- 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}`);
40494
41417
  try {
40495
41418
  await downloadKnowledgeFile(url, zipPath, ZIP_MAX_BYTES, `${id2} zip archive`);
40496
41419
  const entries = await listArchiveEntries(zipPath);
@@ -40500,7 +41423,7 @@ async function fetchZippedXml(id2, url, pattern) {
40500
41423
  const declaredSize = await archiveEntrySize(zipPath, selected);
40501
41424
  if (declaredSize > XML_MAX_BYTES)
40502
41425
  throw new Error(`${id2} xml exceeded the ${XML_MAX_BYTES}-byte expanded limit`);
40503
- mkdirSync10(staging, {
41426
+ mkdirSync11(staging, {
40504
41427
  recursive: true
40505
41428
  });
40506
41429
  const extracted = await runCapturedProcess("unzip", ["-o", "-q", zipPath, selected, "-d", staging], {
@@ -40511,26 +41434,26 @@ async function fetchZippedXml(id2, url, pattern) {
40511
41434
  throw new Error(`${id2} archive extraction timed out`);
40512
41435
  if (extracted.exitCode !== 0)
40513
41436
  throw new Error(extracted.stderr.trim() || `${id2} archive extraction failed`);
40514
- const stagedPath = resolve9(staging, selected);
41437
+ const stagedPath = resolve10(staging, selected);
40515
41438
  const stagedRelative = relative10(staging, stagedPath);
40516
41439
  if (stagedRelative.startsWith("..") || stagedRelative.startsWith("/"))
40517
41440
  throw new Error(`${id2} archive entry escaped the extraction directory`);
40518
- const info = lstatSync9(stagedPath);
41441
+ const info = lstatSync10(stagedPath);
40519
41442
  if (!info.isFile() || info.isSymbolicLink())
40520
41443
  throw new Error(`${id2} archive entry was not a regular file`);
40521
41444
  if (info.size > XML_MAX_BYTES || info.size !== declaredSize)
40522
41445
  throw new Error(`${id2} archive entry size did not match its validated metadata`);
40523
- const path = join28(dir, `${id2}.xml`);
40524
- renameSync7(stagedPath, path);
41446
+ const path = join30(dir, `${id2}.xml`);
41447
+ renameSync8(stagedPath, path);
40525
41448
  return {
40526
41449
  xml: readBoundedXml(path),
40527
41450
  path
40528
41451
  };
40529
41452
  } finally {
40530
41453
  try {
40531
- unlinkSync6(zipPath);
41454
+ unlinkSync7(zipPath);
40532
41455
  } catch {}
40533
- rmSync7(staging, {
41456
+ rmSync8(staging, {
40534
41457
  recursive: true,
40535
41458
  force: true
40536
41459
  });
@@ -40588,7 +41511,7 @@ function readBoundedXml(path) {
40588
41511
  }
40589
41512
  var ZIP_MAX_BYTES, XML_MAX_BYTES, ZIP_LIST_MAX_BYTES, ZIP_ENTRY_MAX_COUNT = 4096, UNZIP_TIMEOUT_MS = 60000;
40590
41513
  var init_zip_fetch = __esm(() => {
40591
- init_paths2();
41514
+ init_paths3();
40592
41515
  init_http2();
40593
41516
  init_captured_process();
40594
41517
  init_file_read();
@@ -40891,7 +41814,7 @@ var init_command = __esm(() => {
40891
41814
  init_pack();
40892
41815
  init_taxonomy_pack();
40893
41816
  init_store();
40894
- init_paths2();
41817
+ init_paths3();
40895
41818
  init_hacktricks();
40896
41819
  init_payloads();
40897
41820
  init_attack();
@@ -40911,6 +41834,80 @@ var init_command = __esm(() => {
40911
41834
  CORPUS_INGEST = new Set(["hacktricks", "payloads", ...TAXONOMY_INGEST]);
40912
41835
  });
40913
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
+
40914
41911
  // node_modules/solid-js/dist/solid.js
40915
41912
  function getContextId(count2) {
40916
41913
  const num2 = String(count2), len = num2.length - 1;
@@ -47978,7 +48975,7 @@ function presentToolActivity(input) {
47978
48975
  const text2 = active ? input.liveOutput ?? input.result ?? "" : input.result ?? input.fullResult ?? "";
47979
48976
  const metadata = input.toolResult?.metadata ?? {};
47980
48977
  const family = toolFamily(tool);
47981
- const title = toolTitle(tool, args, input.status, 240);
48978
+ const title = tool === "email_wait" && !active && metadata.timedOut === true ? emailWaitTimeoutTitle(args) : toolTitle(tool, args, input.status, 240);
47982
48979
  const warning = hasWarning(input.toolResult, text2);
47983
48980
  const preview = toolPreview(tool, args, text2, metadata, active);
47984
48981
  const outcome = toolOutcome(tool, args, text2, metadata, input.toolResult, active);
@@ -48137,6 +49134,10 @@ function compactActivityLabel(tool, args, title) {
48137
49134
  return `flow ${stringValue2(args.flowId) ?? "request"}`;
48138
49135
  return title;
48139
49136
  }
49137
+ function emailWaitTimeoutTitle(args) {
49138
+ const filter = stringValue2(args.subject) ?? stringValue2(args.from);
49139
+ return truncateTerminal(`no matching email${filter ? ` \xB7 ${filter}` : ""}`, 240);
49140
+ }
48140
49141
  function toolOutcome(tool, args, text2, metadata, result, active) {
48141
49142
  if (active)
48142
49143
  return liveOutcome(text2);
@@ -48408,6 +49409,8 @@ function proxyOutcome(metadata, summary2, text2) {
48408
49409
  function hasWarning(result, text2) {
48409
49410
  if (!result)
48410
49411
  return false;
49412
+ if (result.metadata?.emailAction === "wait" && result.metadata.timedOut === true)
49413
+ return true;
48411
49414
  if (result.metadata?.exactProtocolVerificationRequired === true || result.metadata?.snapshotError)
48412
49415
  return true;
48413
49416
  if (arrayValue2(result.metadata?.failures).length > 0)
@@ -48498,6 +49501,7 @@ function formatBytes(bytes) {
48498
49501
  var BROWSER_TOOLS, WORKSPACE_TOOLS, RECON_TOOLS, HTTP_TOOLS;
48499
49502
  var init_tool_activity = __esm(() => {
48500
49503
  init_tool_names();
49504
+ init_terminal_text();
48501
49505
  init_tool_presentation();
48502
49506
  BROWSER_TOOLS = new Set(["browser_navigate", "browser_snapshot", "browser_find", "browser_click", "browser_fill_form", "browser_type", "browser_press_key", "browser_wait_for", "browser_tabs", "browser_network_requests", "browser_network_request"]);
48503
49507
  WORKSPACE_TOOLS = new Set(["fs_read", "fs_list", "fs_grep", "fs_write", "fs_edit", "patch_apply", "notebook_edit", "git_status", "git_diff", "lsp_inspect", "tool_output_read", "code_write_script"]);
@@ -49524,10 +50528,10 @@ function artifactRow(part, width) {
49524
50528
  return row;
49525
50529
  }
49526
50530
  const note = extractObject(payload, "note");
49527
- const artifact = extractObject(payload, "artifact") ?? extractObject(payload, "outputArtifact");
49528
- const title = artifactTitle(kind, note, artifact);
49529
- const detail = firstSemanticLine(stringField2(note, "text")) ?? stringField2(artifact, "path") ?? extractField(payload, "path") ?? extractField(payload, "title") ?? humanLabel(kind);
49530
- 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);
49531
50535
  return {
49532
50536
  kind: "artifact",
49533
50537
  title,
@@ -49538,8 +50542,8 @@ function artifactRow(part, width) {
49538
50542
  id: part.id
49539
50543
  };
49540
50544
  }
49541
- function artifactBody(payload, note, artifact) {
49542
- 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")];
49543
50547
  return candidates.find((value) => Boolean(value?.trim()))?.trim();
49544
50548
  }
49545
50549
  function firstSemanticLine(value) {
@@ -49554,10 +50558,10 @@ function mcpInventoryRow(part, width) {
49554
50558
  id: part.id
49555
50559
  };
49556
50560
  }
49557
- function artifactTitle(kind, note, artifact) {
50561
+ function artifactTitle(kind, note, artifact2) {
49558
50562
  if (stringField2(note, "text"))
49559
50563
  return "saved note";
49560
- if (stringField2(artifact, "path"))
50564
+ if (stringField2(artifact2, "path"))
49561
50565
  return "saved artifact";
49562
50566
  return humanLabel(kind);
49563
50567
  }
@@ -50133,7 +51137,7 @@ function createStoreResourceController(input) {
50133
51137
  actions.servicesSet(services.value);
50134
51138
  if (catalog.status === "fulfilled")
50135
51139
  actions.mcpCatalogSet(catalog.value.servers, catalog.value.statuses);
50136
- 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));
50137
51141
  actions.mcpStatusErrorSet(errors.length ? [...new Set(errors)].join(" \xB7 ") : undefined);
50138
51142
  } finally {
50139
51143
  if (mcpOverlayGeneration === generation && sessions2.owns(owner) && store.ui.statusDetail === "refreshing mcp") {
@@ -50155,7 +51159,7 @@ function createStoreResourceController(input) {
50155
51159
  actions.emailCatalogSet(catalog.accounts);
50156
51160
  } catch (error) {
50157
51161
  if (emailOverlayGeneration === generation && sessions2.owns(owner))
50158
- actions.errorSet(errorMessage6(error));
51162
+ actions.errorSet(errorMessage8(error));
50159
51163
  } finally {
50160
51164
  if (emailOverlayGeneration === generation && sessions2.owns(owner) && store.ui.statusDetail === "loading email") {
50161
51165
  input.setStatusDetail(undefined);
@@ -50201,7 +51205,7 @@ function createStoreResourceController(input) {
50201
51205
  if (!sessions2.owns(owner))
50202
51206
  return;
50203
51207
  if (options.reportError !== false) {
50204
- actions.errorSet(errorMessage6(error));
51208
+ actions.errorSet(errorMessage8(error));
50205
51209
  return;
50206
51210
  }
50207
51211
  throw error;
@@ -50244,7 +51248,7 @@ function containerState(imageExists, imageContractCurrent, persistentRunning, pe
50244
51248
  return "missing";
50245
51249
  return persistentRunning && persistentImageCurrent ? "running" : "stopped";
50246
51250
  }
50247
- function errorMessage6(error) {
51251
+ function errorMessage8(error) {
50248
51252
  return error instanceof Error ? error.message : String(error);
50249
51253
  }
50250
51254
 
@@ -50288,7 +51292,7 @@ function createStorePromptController(input) {
50288
51292
  await port.prompt(sessionId, text2);
50289
51293
  } catch (error) {
50290
51294
  if (isActiveSession(sessionId))
50291
- actions.errorSet(errorMessage7(error));
51295
+ actions.errorSet(errorMessage9(error));
50292
51296
  } finally {
50293
51297
  if (submissions.get(sessionId) !== submission)
50294
51298
  return;
@@ -50300,7 +51304,7 @@ function createStorePromptController(input) {
50300
51304
  await sessions2.requestSnapshotRefresh(sessionId);
50301
51305
  } catch (error) {
50302
51306
  if (isActiveSession(sessionId))
50303
- actions.errorSet(errorMessage7(error));
51307
+ actions.errorSet(errorMessage9(error));
50304
51308
  }
50305
51309
  }
50306
51310
  })();
@@ -50323,7 +51327,7 @@ function createStorePromptController(input) {
50323
51327
  return submitted;
50324
51328
  } catch (error) {
50325
51329
  if (sessions2.owns(owner))
50326
- actions.errorSet(errorMessage7(error));
51330
+ actions.errorSet(errorMessage9(error));
50327
51331
  return false;
50328
51332
  } finally {
50329
51333
  if (sessions2.owns(owner) && store.ui.statusDetail === status2)
@@ -50350,7 +51354,7 @@ function createStorePromptController(input) {
50350
51354
  } catch (error) {
50351
51355
  if (sessions2.owns(owner) && store.snapshot.pendingUserInput?.id === requestId) {
50352
51356
  actions.requestUserInputSubmittingSet(false);
50353
- actions.errorSet(errorMessage7(error));
51357
+ actions.errorSet(errorMessage9(error));
50354
51358
  }
50355
51359
  return false;
50356
51360
  }
@@ -50393,7 +51397,7 @@ function createStorePromptController(input) {
50393
51397
  await sessions2.requestSnapshotRefresh(owner.sessionId);
50394
51398
  } catch (error) {
50395
51399
  if (sessions2.owns(owner) && store.snapshot.pendingUserInput?.id === requestId)
50396
- actions.errorSet(errorMessage7(error));
51400
+ actions.errorSet(errorMessage9(error));
50397
51401
  }
50398
51402
  }
50399
51403
  function queuePrompt(text2) {
@@ -50411,7 +51415,7 @@ function createStorePromptController(input) {
50411
51415
  return true;
50412
51416
  } catch (error) {
50413
51417
  if (owner && sessions2.owns(owner))
50414
- actions.errorSet(errorMessage7(error));
51418
+ actions.errorSet(errorMessage9(error));
50415
51419
  return false;
50416
51420
  }
50417
51421
  }
@@ -50437,7 +51441,7 @@ function createStorePromptController(input) {
50437
51441
  } catch (error) {
50438
51442
  if (!sessions2.owns(owner))
50439
51443
  return;
50440
- const message = errorMessage7(error);
51444
+ const message = errorMessage9(error);
50441
51445
  if (!/abort|cancel/i.test(message))
50442
51446
  actions.errorSet(message);
50443
51447
  } finally {
@@ -50462,7 +51466,7 @@ function createStorePromptController(input) {
50462
51466
  input.setStatusDetail("conversation cleared", 1500);
50463
51467
  } catch (error) {
50464
51468
  if (sessions2.owns(owner))
50465
- actions.errorSet(errorMessage7(error));
51469
+ actions.errorSet(errorMessage9(error));
50466
51470
  }
50467
51471
  }
50468
51472
  async function cancelCurrentTurn() {
@@ -50485,7 +51489,7 @@ function createStorePromptController(input) {
50485
51489
  await port.cancelTurn(turnId, "cancelled by user");
50486
51490
  } catch (error) {
50487
51491
  if (sessions2.owns(owner))
50488
- actions.errorSet(errorMessage7(error));
51492
+ actions.errorSet(errorMessage9(error));
50489
51493
  }
50490
51494
  if (!sessions2.owns(owner))
50491
51495
  return;
@@ -50493,7 +51497,7 @@ function createStorePromptController(input) {
50493
51497
  await sessions2.requestSnapshotRefresh(owner.sessionId);
50494
51498
  } catch (error) {
50495
51499
  if (sessions2.owns(owner))
50496
- actions.errorSet(errorMessage7(error));
51500
+ actions.errorSet(errorMessage9(error));
50497
51501
  }
50498
51502
  }
50499
51503
  return {
@@ -50517,7 +51521,7 @@ function mergeQueuedPrompts(current, queued) {
50517
51521
  return current;
50518
51522
  return [...current, queued].sort((left, right) => left.sequence - right.sequence);
50519
51523
  }
50520
- function errorMessage7(error) {
51524
+ function errorMessage9(error) {
50521
51525
  return error instanceof Error ? error.message : String(error);
50522
51526
  }
50523
51527
  var init_store_prompt_controller = __esm(() => {
@@ -54204,9 +55208,9 @@ function parseCommandArguments(value) {
54204
55208
 
54205
55209
  // src/agent-tui/clipboard.ts
54206
55210
  import { spawnSync } from "child_process";
54207
- import { mkdtempSync, rmSync as rmSync8, writeFileSync as writeFileSync3 } from "fs";
55211
+ import { mkdtempSync, rmSync as rmSync9, writeFileSync as writeFileSync3 } from "fs";
54208
55212
  import { tmpdir as tmpdir2 } from "os";
54209
- import { join as join29 } from "path";
55213
+ import { join as join31 } from "path";
54210
55214
  function writeClipboard(text2) {
54211
55215
  if (!text2)
54212
55216
  return {
@@ -54258,8 +55262,8 @@ function runClipboardCommand(command, text2) {
54258
55262
  };
54259
55263
  }
54260
55264
  function writeClipboardWithAppleScript(text2) {
54261
- const dir = mkdtempSync(join29(tmpdir2(), "farai-clipboard-"));
54262
- const path = join29(dir, "clipboard.txt");
55265
+ const dir = mkdtempSync(join31(tmpdir2(), "farai-clipboard-"));
55266
+ const path = join31(dir, "clipboard.txt");
54263
55267
  try {
54264
55268
  writeFileSync3(path, text2, {
54265
55269
  encoding: "utf8",
@@ -54283,7 +55287,7 @@ function writeClipboardWithAppleScript(text2) {
54283
55287
  error: `osascript: ${detail}`
54284
55288
  };
54285
55289
  } finally {
54286
- rmSync8(dir, {
55290
+ rmSync9(dir, {
54287
55291
  recursive: true,
54288
55292
  force: true
54289
55293
  });
@@ -54304,9 +55308,9 @@ function ctrlCDecision(text2, armedUntil, now = Date.now()) {
54304
55308
  }
54305
55309
 
54306
55310
  // src/agent-tui/input/composer-controller.ts
54307
- 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";
54308
55312
  import { tmpdir as tmpdir3 } from "os";
54309
- import { join as join30 } from "path";
55313
+ import { join as join32 } from "path";
54310
55314
  function createComposerController(input) {
54311
55315
  const {
54312
55316
  tui,
@@ -54574,8 +55578,8 @@ function createComposerController(input) {
54574
55578
  });
54575
55579
  return;
54576
55580
  }
54577
- const dir = mkdtempSync2(join30(tmpdir3(), "farai-editor-"));
54578
- const file = join30(dir, "prompt.md");
55581
+ const dir = mkdtempSync2(join32(tmpdir3(), "farai-editor-"));
55582
+ const file = join32(dir, "prompt.md");
54579
55583
  writeFileSync4(file, composer.ref()?.plainText ?? composer.text(), {
54580
55584
  encoding: "utf8",
54581
55585
  mode: 384
@@ -54609,7 +55613,7 @@ function createComposerController(input) {
54609
55613
  renderer.resume();
54610
55614
  } catch {}
54611
55615
  try {
54612
- rmSync9(dir, {
55616
+ rmSync10(dir, {
54613
55617
  recursive: true,
54614
55618
  force: true
54615
55619
  });
@@ -55418,7 +56422,7 @@ var init_theme = __esm(() => {
55418
56422
  warning: "#d7af5f",
55419
56423
  error: "#ff5f5f",
55420
56424
  success: "#87d75f",
55421
- bg: "#000000",
56425
+ bg: "transparent",
55422
56426
  backdrop: "rgba(0,0,0,0.5)",
55423
56427
  panel: "#101010",
55424
56428
  userMessageBg: "#1f1f1f",
@@ -57458,7 +58462,7 @@ function ToolResultContext(props) {
57458
58462
  const evidence = () => result()?.evidence ?? [];
57459
58463
  const attachments = () => result()?.attachments ?? [];
57460
58464
  const metadata = () => result()?.metadata ?? {};
57461
- const artifact = () => {
58465
+ const artifact2 = () => {
57462
58466
  const value = metadata().outputArtifact;
57463
58467
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
57464
58468
  };
@@ -57500,7 +58504,7 @@ function ToolResultContext(props) {
57500
58504
  }), null);
57501
58505
  insert(_el$31, createComponent2(Show, {
57502
58506
  get when() {
57503
- return result()?.outputArtifactId || artifact();
58507
+ return result()?.outputArtifactId || artifact2();
57504
58508
  },
57505
58509
  get children() {
57506
58510
  var _el$35 = createElement("box"), _el$36 = createElement("text");
@@ -57516,7 +58520,7 @@ function ToolResultContext(props) {
57516
58520
  ...result()?.outputArtifactId ? {
57517
58521
  id: result()?.outputArtifactId
57518
58522
  } : {},
57519
- ...artifact() ?? {}
58523
+ ...artifact2() ?? {}
57520
58524
  });
57521
58525
  },
57522
58526
  children: (line) => (() => {
@@ -61524,7 +62528,7 @@ var init_composer2 = __esm(() => {
61524
62528
  focusTintStart = [22, 33, 38];
61525
62529
  focusTintEnd = [16, 16, 16];
61526
62530
  inactiveComposerBackground = RGBA.fromInts(...focusTintEnd);
61527
- composerPageBackground = RGBA.fromInts(0, 0, 0);
62531
+ composerPageBackground = RGBA.fromInts(0, 0, 0, 0);
61528
62532
  focusTintPalettes = new Map;
61529
62533
  composerEdgeRuns = new Map;
61530
62534
  });
@@ -65890,7 +66894,7 @@ var init_app = __esm(() => {
65890
66894
  });
65891
66895
 
65892
66896
  // src/agent-tui/update-check.ts
65893
- import { dirname as dirname10, join as join31 } from "path";
66897
+ import { dirname as dirname11, join as join33 } from "path";
65894
66898
  function prepareUpdateCheck(options = {}) {
65895
66899
  if (updateCheckDisabled())
65896
66900
  return {
@@ -65933,42 +66937,9 @@ function createUpdateNotice(currentVersion, latestVersion) {
65933
66937
  updateCommand: "npm install -g farai@latest"
65934
66938
  };
65935
66939
  }
65936
- function compareSemver(left, right) {
65937
- const a = parseSemver(left);
65938
- const b = parseSemver(right);
65939
- if (!a || !b)
65940
- return 0;
65941
- for (let index = 0;index < 3; index += 1) {
65942
- const delta = a.core[index] - b.core[index];
65943
- if (delta !== 0)
65944
- return delta < 0 ? -1 : 1;
65945
- }
65946
- if (a.prerelease.length === 0 || b.prerelease.length === 0) {
65947
- if (a.prerelease.length === b.prerelease.length)
65948
- return 0;
65949
- return a.prerelease.length === 0 ? 1 : -1;
65950
- }
65951
- const length = Math.max(a.prerelease.length, b.prerelease.length);
65952
- for (let index = 0;index < length; index += 1) {
65953
- const aPart = a.prerelease[index];
65954
- const bPart = b.prerelease[index];
65955
- if (aPart === undefined || bPart === undefined)
65956
- return aPart === undefined ? -1 : 1;
65957
- if (aPart === bPart)
65958
- continue;
65959
- const aNumber = numericIdentifier(aPart);
65960
- const bNumber = numericIdentifier(bPart);
65961
- if (aNumber !== undefined && bNumber !== undefined)
65962
- return aNumber < bNumber ? -1 : 1;
65963
- if (aNumber !== undefined || bNumber !== undefined)
65964
- return aNumber !== undefined ? -1 : 1;
65965
- return aPart < bPart ? -1 : 1;
65966
- }
65967
- return 0;
65968
- }
65969
66940
  function readUpdateCache(path = updateCachePath()) {
65970
66941
  try {
65971
- ensurePrivateDirectory(dirname10(path), "update cache directory");
66942
+ ensurePrivateDirectory(dirname11(path), "update cache directory");
65972
66943
  ensurePrivateRegularFileIfExists(path, "update cache");
65973
66944
  const parsed = JSON.parse(readBoundedFileTextSyncNoFollow(path, UPDATE_RESPONSE_MAX_BYTES, "update cache"));
65974
66945
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
@@ -65976,7 +66947,7 @@ function readUpdateCache(path = updateCachePath()) {
65976
66947
  const value = parsed;
65977
66948
  if (typeof value.checkedAt !== "number" || !Number.isFinite(value.checkedAt))
65978
66949
  return;
65979
- if (typeof value.latestVersion !== "string" || !parseSemver(value.latestVersion))
66950
+ if (typeof value.latestVersion !== "string" || !isSemver(value.latestVersion))
65980
66951
  return;
65981
66952
  return {
65982
66953
  checkedAt: value.checkedAt,
@@ -65987,13 +66958,13 @@ function readUpdateCache(path = updateCachePath()) {
65987
66958
  }
65988
66959
  }
65989
66960
  function updateCachePath() {
65990
- return join31(globalDataDir(), "update.json");
66961
+ return join33(globalDataDir(), "update.json");
65991
66962
  }
65992
66963
  function readCurrentVersion() {
65993
66964
  try {
65994
- const packagePath = join31(import.meta.dir, "..", "..", "package.json");
66965
+ const packagePath = join33(import.meta.dir, "..", "..", "package.json");
65995
66966
  const parsed = JSON.parse(readBoundedFileTextSync(packagePath, 1024 * 1024, "package metadata"));
65996
- return typeof parsed.version === "string" && parseSemver(parsed.version) ? parsed.version : undefined;
66967
+ return typeof parsed.version === "string" && isSemver(parsed.version) ? parsed.version : undefined;
65997
66968
  } catch {
65998
66969
  return;
65999
66970
  }
@@ -66029,7 +67000,7 @@ async function fetchLatestVersion(fetcher, timeoutMs) {
66029
67000
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
66030
67001
  throw new Error("invalid npm registry response");
66031
67002
  const version = parsed.version;
66032
- if (typeof version !== "string" || !parseSemver(version))
67003
+ if (typeof version !== "string" || !isSemver(version))
66033
67004
  throw new Error("invalid npm package version");
66034
67005
  return version;
66035
67006
  } finally {
@@ -66038,7 +67009,7 @@ async function fetchLatestVersion(fetcher, timeoutMs) {
66038
67009
  }
66039
67010
  function writeUpdateCache(path, cache) {
66040
67011
  try {
66041
- ensurePrivateDirectory(dirname10(path), "update cache directory");
67012
+ ensurePrivateDirectory(dirname11(path), "update cache directory");
66042
67013
  ensurePrivateRegularFileIfExists(path, "update cache");
66043
67014
  atomicWriteFile(path, `${JSON.stringify(cache)}
66044
67015
  `, 384);
@@ -66054,20 +67025,6 @@ function updateCheckDisabled() {
66054
67025
  function envEnabled(value) {
66055
67026
  return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
66056
67027
  }
66057
- function parseSemver(value) {
66058
- 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-]+)*)?$/);
66059
- if (!match)
66060
- return;
66061
- return {
66062
- core: [Number(match[1]), Number(match[2]), Number(match[3])],
66063
- prerelease: match[4]?.split(".") ?? []
66064
- };
66065
- }
66066
- function numericIdentifier(value) {
66067
- if (!/^(0|[1-9]\d*)$/.test(value))
66068
- return;
66069
- return Number(value);
66070
- }
66071
67028
  var UPDATE_CACHE_TTL_MS, UPDATE_CHECK_TIMEOUT_MS = 4000, UPDATE_REGISTRY_URL = "https://registry.npmjs.org/farai/latest", UPDATE_RESPONSE_MAX_BYTES;
66072
67029
  var init_update_check = __esm(() => {
66073
67030
  init_config();
@@ -66134,8 +67091,8 @@ async function runOpenTui(input) {
66134
67091
  const renderer = managedRenderer.renderer;
66135
67092
  const updateCheck = prepareUpdateCheck();
66136
67093
  let done;
66137
- const finished = new Promise((resolve10) => {
66138
- done = resolve10;
67094
+ const finished = new Promise((resolve11) => {
67095
+ done = resolve11;
66139
67096
  });
66140
67097
  let exitPromise;
66141
67098
  const onSigint = () => {
@@ -66376,6 +67333,80 @@ no sessions are available in this workspace`}`);
66376
67333
  };
66377
67334
  });
66378
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
+
66379
67410
  // src/agent-benchmark/csi-cybench-33.ts
66380
67411
  var figureTimeout = (minutes, line) => ({
66381
67412
  status: "verified",
@@ -66637,40 +67668,40 @@ var init_csi_cybench_33 = __esm(() => {
66637
67668
  });
66638
67669
 
66639
67670
  // src/agent-benchmark/hash.ts
66640
- import { createHash as createHash12 } from "crypto";
66641
- import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync10, openSync as openSync6, readSync as readSync2, readdirSync as readdirSync11 } from "fs";
66642
- 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";
66643
67674
  function stableStringify(value) {
66644
67675
  return JSON.stringify(sortValue(value));
66645
67676
  }
66646
67677
  function sha256(value) {
66647
- return createHash12("sha256").update(value).digest("hex");
67678
+ return createHash13("sha256").update(value).digest("hex");
66648
67679
  }
66649
67680
  function hashPath(path) {
66650
- const stat = lstatSync10(path);
67681
+ const stat = lstatSync11(path);
66651
67682
  if (stat.isFile())
66652
- return hashFile(path);
67683
+ return hashFile2(path);
66653
67684
  if (!stat.isDirectory())
66654
67685
  throw new Error(`unsupported benchmark input type: ${path}`);
66655
- const hash = createHash12("sha256");
67686
+ const hash = createHash13("sha256");
66656
67687
  hash.update("farai-directory-v2\x00");
66657
67688
  hashDirectory(path, Buffer.alloc(0), hash);
66658
67689
  return hash.digest("hex");
66659
67690
  }
66660
- function hashFile(path) {
67691
+ function hashFile2(path) {
66661
67692
  return hashFileDetails(path).digest;
66662
67693
  }
66663
67694
  function hashFileDetails(path) {
66664
- const descriptor = openSync6(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
67695
+ const descriptor = openSync7(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
66665
67696
  try {
66666
67697
  const before = fstatSync3(descriptor);
66667
67698
  if (!before.isFile())
66668
67699
  throw new Error(`unsupported benchmark input type: ${path}`);
66669
- const hash = createHash12("sha256");
67700
+ const hash = createHash13("sha256");
66670
67701
  let remaining2 = before.size;
66671
67702
  while (remaining2 > 0) {
66672
67703
  const chunk = Buffer.allocUnsafe(Math.min(1024 * 1024, remaining2));
66673
- const count2 = readSync2(descriptor, chunk, 0, chunk.length, null);
67704
+ const count2 = readSync3(descriptor, chunk, 0, chunk.length, null);
66674
67705
  if (count2 === 0)
66675
67706
  throw new Error(`benchmark input changed while hashing: ${path}`);
66676
67707
  hash.update(chunk.subarray(0, count2));
@@ -66686,7 +67717,7 @@ function hashFileDetails(path) {
66686
67717
  size: before.size
66687
67718
  };
66688
67719
  } finally {
66689
- closeSync6(descriptor);
67720
+ closeSync7(descriptor);
66690
67721
  }
66691
67722
  }
66692
67723
  function canonicalBenchmarkManifest(manifest) {
@@ -66741,22 +67772,22 @@ function sortValue(value) {
66741
67772
  return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, sortValue(item)]));
66742
67773
  }
66743
67774
  function hashDirectory(path, localPath, hash) {
66744
- const before = lstatSync10(path);
67775
+ const before = lstatSync11(path);
66745
67776
  if (!before.isDirectory() || before.isSymbolicLink())
66746
67777
  throw new Error(`unsupported benchmark input type: ${path}`);
66747
67778
  hash.update("directory\x00");
66748
67779
  hash.update(localPath);
66749
67780
  hash.update(`\x00${before.mode & 511}\x00`);
66750
- const names = readdirSync11(path, {
67781
+ const names = readdirSync12(path, {
66751
67782
  encoding: "buffer"
66752
67783
  }).map((name) => Buffer.from(name)).sort(Buffer.compare);
66753
67784
  for (const encodedName of names) {
66754
67785
  const name = encodedName.toString("utf8");
66755
67786
  if (!Buffer.from(name, "utf8").equals(encodedName))
66756
67787
  throw new Error(`benchmark input path is not valid utf-8: ${path}`);
66757
- const childPath = join32(path, name);
67788
+ const childPath = join34(path, name);
66758
67789
  const childLocalPath = localPath.length === 0 ? encodedName : Buffer.concat([localPath, Buffer.from("/"), encodedName]);
66759
- const stat = lstatSync10(childPath);
67790
+ const stat = lstatSync11(childPath);
66760
67791
  if (stat.isDirectory() && !stat.isSymbolicLink()) {
66761
67792
  hashDirectory(childPath, childLocalPath, hash);
66762
67793
  continue;
@@ -66768,7 +67799,7 @@ function hashDirectory(path, localPath, hash) {
66768
67799
  hash.update(childLocalPath);
66769
67800
  hash.update(`\x00${file.mode}\x00${file.size}\x00${file.digest}\x00`);
66770
67801
  }
66771
- const after = lstatSync10(path);
67802
+ const after = lstatSync11(path);
66772
67803
  if (!after.isDirectory() || after.dev !== before.dev || after.ino !== before.ino || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) {
66773
67804
  throw new Error(`benchmark input changed while hashing: ${path}`);
66774
67805
  }
@@ -66776,7 +67807,7 @@ function hashDirectory(path, localPath, hash) {
66776
67807
  var init_hash = () => {};
66777
67808
 
66778
67809
  // src/agent-benchmark/manifest.ts
66779
- import { isAbsolute as isAbsolute7, normalize as normalize3 } from "path";
67810
+ import { isAbsolute as isAbsolute8, normalize as normalize3 } from "path";
66780
67811
  async function loadBenchmarkManifest(path) {
66781
67812
  return normalizeBenchmarkManifest(JSON.parse(await readBoundedFileText(path, BENCHMARK_MANIFEST_MAX_BYTES, "benchmark manifest")));
66782
67813
  }
@@ -66815,27 +67846,27 @@ function normalizeBenchmarkManifest(value) {
66815
67846
  id: requiredString(suite.id, "suite.id"),
66816
67847
  version: requiredString(suite.version, "suite.version"),
66817
67848
  source: requiredString(suite.source, "suite.source"),
66818
- ...optionalString2(suite.sourceDigest ?? suite.source_digest) ? {
66819
- sourceDigest: optionalString2(suite.sourceDigest ?? suite.source_digest)
67849
+ ...optionalString3(suite.sourceDigest ?? suite.source_digest) ? {
67850
+ sourceDigest: optionalString3(suite.sourceDigest ?? suite.source_digest)
66820
67851
  } : {}
66821
67852
  },
66822
67853
  challenge: {
66823
67854
  id: requiredString(challenge.id, "challenge.id"),
66824
67855
  prompt: requiredString(challenge.prompt, "challenge.prompt"),
66825
- ...optionalString2(challenge.category) ? {
66826
- category: optionalString2(challenge.category)
67856
+ ...optionalString3(challenge.category) ? {
67857
+ category: optionalString3(challenge.category)
66827
67858
  } : {},
66828
- ...optionalString2(challenge.difficulty) ? {
66829
- difficulty: optionalString2(challenge.difficulty)
67859
+ ...optionalString3(challenge.difficulty) ? {
67860
+ difficulty: optionalString3(challenge.difficulty)
66830
67861
  } : {},
66831
- ...optionalString2(challenge.source) ? {
66832
- source: optionalString2(challenge.source)
67862
+ ...optionalString3(challenge.source) ? {
67863
+ source: optionalString3(challenge.source)
66833
67864
  } : {},
66834
- ...optionalString2(challenge.targetImage ?? challenge.target_image) ? {
66835
- targetImage: optionalString2(challenge.targetImage ?? challenge.target_image)
67865
+ ...optionalString3(challenge.targetImage ?? challenge.target_image) ? {
67866
+ targetImage: optionalString3(challenge.targetImage ?? challenge.target_image)
66836
67867
  } : {},
66837
- ...optionalString2(challenge.targetImageDigest ?? challenge.target_image_digest) ? {
66838
- 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)
66839
67870
  } : {},
66840
67871
  ...challenge.targetCommand ?? challenge.target_command ? {
66841
67872
  targetCommand: stringArray3(challenge.targetCommand ?? challenge.target_command, "challenge.targetCommand")
@@ -66843,11 +67874,11 @@ function normalizeBenchmarkManifest(value) {
66843
67874
  },
66844
67875
  model: {
66845
67876
  selection: requiredString(model.selection, "model.selection"),
66846
- ...optionalString2(model.provider) ? {
66847
- provider: optionalString2(model.provider)
67877
+ ...optionalString3(model.provider) ? {
67878
+ provider: optionalString3(model.provider)
66848
67879
  } : {},
66849
- ...optionalString2(model.protocol) ? {
66850
- protocol: optionalString2(model.protocol)
67880
+ ...optionalString3(model.protocol) ? {
67881
+ protocol: optionalString3(model.protocol)
66851
67882
  } : {},
66852
67883
  ...optionalPositiveNumber(model.contextWindow ?? model.context_window, "model.contextWindow") ? {
66853
67884
  contextWindow: optionalPositiveNumber(model.contextWindow ?? model.context_window, "model.contextWindow")
@@ -66906,7 +67937,7 @@ function normalizeBenchmarkSuiteManifest(value) {
66906
67937
  const id2 = requiredString(raw.id, "id");
66907
67938
  const version = requiredString(raw.version, "version");
66908
67939
  const source = requiredString(raw.source, "source");
66909
- const sourceDigest = optionalString2(raw.sourceDigest ?? raw.source_digest);
67940
+ const sourceDigest = optionalString3(raw.sourceDigest ?? raw.source_digest);
66910
67941
  const repetitions = positiveInteger4(raw.repetitions, "repetitions");
66911
67942
  const concurrency = positiveInteger4(raw.concurrency, "concurrency");
66912
67943
  const runs = raw.runs.map((entry, index) => {
@@ -66963,8 +67994,8 @@ function normalizeOracle(value) {
66963
67994
  executableSha256: optionalSha256(raw.executableSha256 ?? raw.executable_sha256, "oracle.executableSha256")
66964
67995
  } : {},
66965
67996
  flagPattern: requiredString(raw.flagPattern ?? raw.flag_pattern, "oracle.flagPattern"),
66966
- ...optionalString2(raw.flags) ? {
66967
- flags: optionalString2(raw.flags)
67997
+ ...optionalString3(raw.flags) ? {
67998
+ flags: optionalString3(raw.flags)
66968
67999
  } : {},
66969
68000
  ...optionalPositiveNumber(raw.timeoutSeconds ?? raw.timeout_seconds, "oracle.timeoutSeconds") ? {
66970
68001
  timeoutSeconds: optionalPositiveNumber(raw.timeoutSeconds ?? raw.timeout_seconds, "oracle.timeoutSeconds")
@@ -67020,7 +68051,7 @@ function optionalResource(raw, key, integer2, snake = key) {
67020
68051
  };
67021
68052
  }
67022
68053
  function safeRelativePath(value) {
67023
- if (isAbsolute7(value))
68054
+ if (isAbsolute8(value))
67024
68055
  throw new Error(`benchmark destination must be relative: ${value}`);
67025
68056
  const normalized = normalize3(value).replace(/\\/g, "/");
67026
68057
  if (!normalized || normalized === "." || normalized.split("/").includes(".."))
@@ -67052,7 +68083,7 @@ function requiredString(value, name) {
67052
68083
  throw new Error(`${name} must be a non-empty string`);
67053
68084
  return value.trim();
67054
68085
  }
67055
- function optionalString2(value) {
68086
+ function optionalString3(value) {
67056
68087
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
67057
68088
  }
67058
68089
  function optionalSha256(value, name) {
@@ -67099,7 +68130,7 @@ function optionalInteger(value, name, minimum) {
67099
68130
  return number;
67100
68131
  }
67101
68132
  var BENCHMARK_MANIFEST_MAX_BYTES;
67102
- var init_manifest = __esm(() => {
68133
+ var init_manifest2 = __esm(() => {
67103
68134
  init_file_read();
67104
68135
  BENCHMARK_MANIFEST_MAX_BYTES = 32 * 1024 * 1024;
67105
68136
  });
@@ -67114,17 +68145,17 @@ __export(exports_csi_suite, {
67114
68145
  loadCsiCampaignConfig: () => loadCsiCampaignConfig,
67115
68146
  generateCsiBenchmarkSuite: () => generateCsiBenchmarkSuite
67116
68147
  });
67117
- import { existsSync as existsSync24, readdirSync as readdirSync12, statSync as statSync6 } from "fs";
67118
- 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";
67119
68150
  async function loadCsiCampaignConfig(path) {
67120
68151
  return normalizeCsiCampaignConfig(JSON.parse(await readBoundedFileText(path, CSI_CAMPAIGN_MAX_BYTES, "csi campaign config")));
67121
68152
  }
67122
68153
  async function loadCsiMaterialIndex(root) {
67123
- 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")));
67124
68155
  }
67125
68156
  async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67126
68157
  const config = normalizeCsiCampaignConfig(configInput);
67127
- const root = resolve10(materialRoot);
68158
+ const root = resolve11(materialRoot);
67128
68159
  const materials = await loadCsiMaterialIndex(root);
67129
68160
  const selectedIds = config.challenges ?? CSI_CYBENCH_33.challenges.map((challenge) => challenge.id);
67130
68161
  const selected = selectedIds.map((id2) => {
@@ -67140,16 +68171,16 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67140
68171
  if (config.isolation.backend === "host" && material.requiresTarget)
67141
68172
  throw new Error(`host csi challenge requires a live target and cannot run in host smoke mode: ${challenge.id}`);
67142
68173
  const promptPath = protectedPath(root, material.promptFile, `${challenge.id}.promptFile`);
67143
- if (!existsSync24(promptPath) || !statSync6(promptPath).isFile())
68174
+ if (!existsSync27(promptPath) || !statSync7(promptPath).isFile())
67144
68175
  throw new Error(`missing prompt file for csi challenge: ${challenge.id}`);
67145
68176
  const prompt = readBoundedFileTextSync(promptPath, CSI_PROMPT_MAX_BYTES, `csi prompt ${challenge.id}`).trim();
67146
68177
  if (!prompt)
67147
68178
  throw new Error(`empty prompt file for csi challenge: ${challenge.id}`);
67148
68179
  const files = material.files?.map((file, index) => {
67149
68180
  const source = protectedPath(root, file.source, `${challenge.id}.files[${index}].source`);
67150
- if (!existsSync24(source))
68181
+ if (!existsSync27(source))
67151
68182
  throw new Error(`missing input for csi challenge ${challenge.id}: ${file.source}`);
67152
- if (statSync6(source).isDirectory() && !listFiles(source).length)
68183
+ if (statSync7(source).isDirectory() && !listFiles(source).length)
67153
68184
  throw new Error(`empty input directory for csi challenge ${challenge.id}: ${file.source}`);
67154
68185
  const digest2 = hashPath(source);
67155
68186
  if (file.sha256 && file.sha256.toLowerCase() !== digest2)
@@ -67169,14 +68200,14 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67169
68200
  throw new Error(`missing required protected files for csi challenge ${challenge.id}: ${missing.join(", ")}`);
67170
68201
  }
67171
68202
  const executable = protectedPath(root, material.oracle.executable, `${challenge.id}.oracle.executable`);
67172
- if (!existsSync24(executable) || !statSync6(executable).isFile())
68203
+ if (!existsSync27(executable) || !statSync7(executable).isFile())
67173
68204
  throw new Error(`missing oracle executable for csi challenge: ${challenge.id}`);
67174
- if ((statSync6(executable).mode & 73) === 0)
68205
+ if ((statSync7(executable).mode & 73) === 0)
67175
68206
  throw new Error(`oracle executable is not executable for csi challenge: ${challenge.id}`);
67176
68207
  const antiCheatExecutable = material.antiCheat ? protectedPath(root, material.antiCheat.executable, `${challenge.id}.antiCheat.executable`) : undefined;
67177
- if (antiCheatExecutable && (!existsSync24(antiCheatExecutable) || !statSync6(antiCheatExecutable).isFile()))
68208
+ if (antiCheatExecutable && (!existsSync27(antiCheatExecutable) || !statSync7(antiCheatExecutable).isFile()))
67178
68209
  throw new Error(`missing anti-cheat executable for csi challenge: ${challenge.id}`);
67179
- if (antiCheatExecutable && (statSync6(antiCheatExecutable).mode & 73) === 0)
68210
+ if (antiCheatExecutable && (statSync7(antiCheatExecutable).mode & 73) === 0)
67180
68211
  throw new Error(`anti-cheat executable is not executable for csi challenge: ${challenge.id}`);
67181
68212
  if (config.isolation.backend === "docker" && !material.target)
67182
68213
  throw new Error(`docker csi challenge requires a pinned target image: ${challenge.id}`);
@@ -67249,8 +68280,8 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
67249
68280
  });
67250
68281
  }
67251
68282
  function writeCsiBenchmarkSuite(suite, path) {
67252
- const directory = dirname11(resolve10(path));
67253
- if (!existsSync24(directory))
68283
+ const directory = dirname12(resolve11(path));
68284
+ if (!existsSync27(directory))
67254
68285
  throw new Error(`suite output directory does not exist: ${directory}`);
67255
68286
  atomicWriteFile(path, `${JSON.stringify(suite, null, 2)}
67256
68287
  `, 384);
@@ -67329,8 +68360,8 @@ function normalizeCsiMaterialIndex(value) {
67329
68360
  args: stringArray4(oracle.args, `challenges.${id2}.oracle.args`, true)
67330
68361
  },
67331
68362
  flagPattern: requiredString2(oracle.flagPattern ?? oracle.flag_pattern, `challenges.${id2}.oracle.flagPattern`),
67332
- ...optionalString3(oracle.flags) ? {
67333
- flags: optionalString3(oracle.flags)
68363
+ ...optionalString4(oracle.flags) ? {
68364
+ flags: optionalString4(oracle.flags)
67334
68365
  } : {},
67335
68366
  ...oracle.timeoutSeconds ?? oracle.timeout_seconds ? {
67336
68367
  timeoutSeconds: positiveNumber3(oracle.timeoutSeconds ?? oracle.timeout_seconds, `challenges.${id2}.oracle.timeoutSeconds`)
@@ -67376,18 +68407,18 @@ function resolveTimeoutMinutes(id2, catalog, material) {
67376
68407
  return material.minutes;
67377
68408
  }
67378
68409
  function protectedPath(root, path, name) {
67379
- if (isAbsolute8(path))
68410
+ if (isAbsolute9(path))
67380
68411
  throw new Error(`${name} must be relative to the protected material root`);
67381
- const resolved = resolve10(root, path);
68412
+ const resolved = resolve11(root, path);
67382
68413
  const difference = relative11(root, resolved);
67383
- if (!difference || difference.startsWith("..") || isAbsolute8(difference))
68414
+ if (!difference || difference.startsWith("..") || isAbsolute9(difference))
67384
68415
  throw new Error(`${name} escapes the protected material root`);
67385
68416
  return resolved;
67386
68417
  }
67387
68418
  function listFiles(rootPath) {
67388
- if (!statSync6(rootPath).isDirectory())
68419
+ if (!statSync7(rootPath).isDirectory())
67389
68420
  return [rootPath];
67390
- return readdirSync12(rootPath).flatMap((name) => listFiles(join33(rootPath, name)));
68421
+ return readdirSync13(rootPath).flatMap((name) => listFiles(join35(rootPath, name)));
67391
68422
  }
67392
68423
  function object2(value, name) {
67393
68424
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -67399,7 +68430,7 @@ function requiredString2(value, name) {
67399
68430
  throw new Error(`${name} must be a non-empty string`);
67400
68431
  return value.trim();
67401
68432
  }
67402
- function optionalString3(value) {
68433
+ function optionalString4(value) {
67403
68434
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
67404
68435
  }
67405
68436
  function stringArray4(value, name, allowEmpty) {
@@ -67441,7 +68472,7 @@ var CSI_CAMPAIGN_MAX_BYTES, CSI_MATERIAL_INDEX_MAX_BYTES, CSI_PROMPT_MAX_BYTES;
67441
68472
  var init_csi_suite = __esm(() => {
67442
68473
  init_csi_cybench_33();
67443
68474
  init_hash();
67444
- init_manifest();
68475
+ init_manifest2();
67445
68476
  init_file_read();
67446
68477
  init_atomic_file();
67447
68478
  CSI_CAMPAIGN_MAX_BYTES = 4 * 1024 * 1024;
@@ -67450,28 +68481,28 @@ var init_csi_suite = __esm(() => {
67450
68481
  });
67451
68482
 
67452
68483
  // src/agent-benchmark/bundle.ts
67453
- import { createHash as createHash13 } from "crypto";
67454
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync11 } from "fs";
67455
- 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";
67456
68487
  function writeBenchmarkBundle(bundle, directory) {
67457
- mkdirSync11(directory, {
68488
+ mkdirSync12(directory, {
67458
68489
  recursive: true
67459
68490
  });
67460
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)]]);
67461
68492
  for (const [name, content] of files)
67462
- atomicWriteFile(join34(directory, name), content, 384);
68493
+ atomicWriteFile(join36(directory, name), content, 384);
67463
68494
  const checksums = [...files.keys()].sort().map((name) => `${sha2562(files.get(name))} ${name}`).join(`
67464
68495
  `);
67465
- atomicWriteFile(join34(directory, "checksums.sha256"), `${checksums}
68496
+ atomicWriteFile(join36(directory, "checksums.sha256"), `${checksums}
67466
68497
  `, 384);
67467
68498
  for (const name of [...files.keys(), "checksums.sha256"])
67468
- chmodSync3(join34(directory, name), 292);
68499
+ chmodSync3(join36(directory, name), 292);
67469
68500
  return directory;
67470
68501
  }
67471
68502
  function writeBenchmarkResult(result, path) {
67472
68503
  const directory = path.slice(0, Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")));
67473
68504
  if (directory)
67474
- mkdirSync11(directory, {
68505
+ mkdirSync12(directory, {
67475
68506
  recursive: true
67476
68507
  });
67477
68508
  atomicWriteFile(path, json(result), 384);
@@ -67489,7 +68520,7 @@ function jsonl(values) {
67489
68520
  ` : "";
67490
68521
  }
67491
68522
  function sha2562(value) {
67492
- return createHash13("sha256").update(value).digest("hex");
68523
+ return createHash14("sha256").update(value).digest("hex");
67493
68524
  }
67494
68525
  var init_bundle = __esm(() => {
67495
68526
  init_hash();
@@ -67497,8 +68528,8 @@ var init_bundle = __esm(() => {
67497
68528
  });
67498
68529
 
67499
68530
  // src/agent-benchmark/docker-lifecycle.ts
67500
- import { existsSync as existsSync25 } from "fs";
67501
- import { resolve as resolve11 } from "path";
68531
+ import { existsSync as existsSync28 } from "fs";
68532
+ import { resolve as resolve12 } from "path";
67502
68533
 
67503
68534
  class BenchmarkDockerLifecycle {
67504
68535
  constructor(manifest, workspace, runId, runner = runProcess2) {
@@ -67616,7 +68647,7 @@ function buildBenchmarkDockerPlan(manifest, workspace, runId, agentImageId) {
67616
68647
  throw new Error("docker benchmark requires a pinned target image");
67617
68648
  if (!manifest.antiCheat)
67618
68649
  throw new Error("docker benchmark requires an external anti-cheat hook");
67619
- if (!existsSync25(manifest.antiCheat.executable))
68650
+ if (!existsSync28(manifest.antiCheat.executable))
67620
68651
  throw new Error("anti-cheat executable is missing");
67621
68652
  if (hashPath(manifest.antiCheat.executable) !== manifest.antiCheat.executableSha256)
67622
68653
  throw new Error("anti-cheat executable hash mismatch");
@@ -67635,7 +68666,7 @@ function buildBenchmarkDockerPlan(manifest, workspace, runId, agentImageId) {
67635
68666
  const resourceArgs = [...resources?.cpus ? ["--cpus", String(resources.cpus)] : [], ...resources?.memoryMb ? ["--memory", `${resources.memoryMb}m`] : []];
67636
68667
  const targetImage = pinnedImage(manifest.challenge.targetImage, manifest.challenge.targetImageDigest);
67637
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 ?? []];
67638
- const resolvedWorkspace = resolve11(workspace);
68669
+ const resolvedWorkspace = resolve12(workspace);
67639
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"];
67640
68671
  return {
67641
68672
  names,
@@ -67685,9 +68716,9 @@ var init_docker_lifecycle = __esm(() => {
67685
68716
  });
67686
68717
 
67687
68718
  // src/agent-benchmark/git-state.ts
67688
- import { createHash as createHash14 } from "crypto";
67689
- import { lstatSync as lstatSync11, readlinkSync } from "fs";
67690
- 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";
67691
68722
  import { spawn as spawn5 } from "child_process";
67692
68723
  async function freezeGitSourceState(root) {
67693
68724
  if (!await isGitWorktree(root))
@@ -67724,7 +68755,7 @@ async function readGitCommit(root) {
67724
68755
  return commit.toLowerCase();
67725
68756
  }
67726
68757
  async function hashGitWorktree(root, hasCommit) {
67727
- const hash = createHash14("sha256");
68758
+ const hash = createHash15("sha256");
67728
68759
  hash.update("farai-git-worktree-v2\x00");
67729
68760
  await hashCommand(root, ["status", "--porcelain=v1", "-z", "--untracked-files=no", "--ignore-submodules=none"], hash, "status");
67730
68761
  await hashCommand(root, ["diff", "--no-ext-diff", "--no-textconv", "--binary", "--full-index", "--submodule=diff", "--"], hash, "unstaged");
@@ -67766,17 +68797,17 @@ function hashUntrackedPath(root, encodedPath, hash) {
67766
68797
  const path = encodedPath.toString("utf8");
67767
68798
  if (!Buffer.from(path, "utf8").equals(encodedPath))
67768
68799
  throw new Error("git returned an untracked path that is not valid utf-8");
67769
- const absolute = resolve12(root, path);
67770
- const local = relative12(resolve12(root), absolute);
67771
- 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)) {
67772
68803
  throw new Error(`git returned an unsafe untracked path: ${path}`);
67773
68804
  }
67774
- const stat = lstatSync11(absolute);
68805
+ const stat = lstatSync12(absolute);
67775
68806
  hash.update("path\x00");
67776
68807
  hash.update(encodedPath);
67777
68808
  hash.update("\x00");
67778
68809
  if (stat.isFile()) {
67779
- 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`);
67780
68811
  return;
67781
68812
  }
67782
68813
  if (stat.isSymbolicLink()) {
@@ -67877,27 +68908,27 @@ __export(exports_runner, {
67877
68908
  normalizeBenchmarkManifest: () => normalizeBenchmarkManifest,
67878
68909
  loadBenchmarkManifest: () => loadBenchmarkManifest
67879
68910
  });
67880
- 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";
67881
68912
  import { arch, platform, tmpdir as tmpdir4 } from "os";
67882
- import { dirname as dirname12, isAbsolute as isAbsolute10, join as join35, relative as relative13, resolve as resolve13 } from "path";
67883
- 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";
67884
68915
  async function runBenchmark(input, options = {}) {
67885
68916
  const manifest = normalizeBenchmarkManifest(input);
67886
68917
  assertExecutableIsolation(manifest);
67887
- const workspace = options.workspace ?? mkdtempSync3(join35(tmpdir4(), "farai-benchmark-"));
67888
- 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-"));
67889
68920
  const repetition = options.repetition ?? 1;
67890
- mkdirSync12(workspace, {
68921
+ mkdirSync13(workspace, {
67891
68922
  recursive: true
67892
68923
  });
67893
68924
  assertCleanWorkspace(workspace);
67894
68925
  assertArtifactsOutsideWorkspace(workspace, artifactsRoot);
67895
- mkdirSync12(artifactsRoot, {
68926
+ mkdirSync13(artifactsRoot, {
67896
68927
  recursive: true
67897
68928
  });
67898
68929
  stageFiles(manifest, workspace);
67899
68930
  const runId = id();
67900
- const bundlePath = join35(artifactsRoot, `${safeName2(manifest.challenge.id)}-r${repetition}-${runId}`);
68931
+ const bundlePath = join37(artifactsRoot, `${safeName2(manifest.challenge.id)}-r${repetition}-${runId}`);
67901
68932
  const provider = options.provider ?? await createChatProviderForSession(syntheticSession(workspace, manifest));
67902
68933
  assertProvider(manifest, provider);
67903
68934
  const dockerLifecycle = manifest.isolation.backend === "docker" ? new BenchmarkDockerLifecycle(manifest, workspace, runId, options.dockerProcessRunner) : undefined;
@@ -67941,7 +68972,7 @@ async function runBenchmark(input, options = {}) {
67941
68972
  session = runtime.updateSession(session.id, {
67942
68973
  toolScope: activeTools.map((tool) => tool.name)
67943
68974
  });
67944
- const faraiRoot = options.faraiRoot ?? resolve13(import.meta.dir, "..", "..");
68975
+ const faraiRoot = options.faraiRoot ?? resolve14(import.meta.dir, "..", "..");
67945
68976
  const frozen = await freezeRun(manifest, session, activeTools, faraiRoot, provider, dockerState?.agentImageId);
67946
68977
  const promptPromise = runtime.prompt(session, manifest.challenge.prompt).then((result2) => {
67947
68978
  response = result2.response;
@@ -68072,13 +69103,13 @@ function assertExecutableIsolation(manifest) {
68072
69103
  }
68073
69104
  }
68074
69105
  function assertCleanWorkspace(workspace) {
68075
- const entries = readdirSync13(workspace);
69106
+ const entries = readdirSync14(workspace);
68076
69107
  if (entries.length)
68077
69108
  throw new Error(`benchmark workspace must be an empty scratch directory: ${workspace}`);
68078
69109
  }
68079
69110
  function assertArtifactsOutsideWorkspace(workspace, artifactsRoot) {
68080
- const difference = relative13(resolve13(workspace), resolve13(artifactsRoot));
68081
- const reverse = relative13(resolve13(artifactsRoot), resolve13(workspace));
69111
+ const difference = relative13(resolve14(workspace), resolve14(artifactsRoot));
69112
+ const reverse = relative13(resolve14(artifactsRoot), resolve14(workspace));
68082
69113
  if (!difference || !difference.startsWith("..") || !reverse.startsWith("..")) {
68083
69114
  throw new Error("benchmark artifacts directory must be outside the scratch workspace");
68084
69115
  }
@@ -68175,18 +69206,18 @@ function collectRunData(runtime, rootSessionId) {
68175
69206
  };
68176
69207
  }
68177
69208
  function stageFiles(manifest, workspace) {
68178
- const root = resolve13(workspace);
68179
- const workspaceStat = lstatSync12(root);
69209
+ const root = resolve14(workspace);
69210
+ const workspaceStat = lstatSync13(root);
68180
69211
  if (!workspaceStat.isDirectory() || workspaceStat.isSymbolicLink())
68181
69212
  throw new Error("benchmark workspace must be a real directory");
68182
69213
  const inputs = (manifest.files ?? []).map((file) => {
68183
- const source = resolve13(file.source);
68184
- if (!existsSync26(source))
69214
+ const source = resolve14(file.source);
69215
+ if (!existsSync29(source))
68185
69216
  throw new Error(`benchmark input does not exist: ${file.source}`);
68186
69217
  const expectedHash = file.sha256?.toLowerCase() ?? hashPath(source);
68187
69218
  if (hashPath(source) !== expectedHash)
68188
69219
  throw new Error(`benchmark input hash mismatch: ${file.source}`);
68189
- const target = resolve13(root, file.destination);
69220
+ const target = resolve14(root, file.destination);
68190
69221
  if (!isDescendantPath(relative13(root, target)))
68191
69222
  throw new Error(`benchmark destination escapes scratch workspace: ${file.destination}`);
68192
69223
  return {
@@ -68208,10 +69239,10 @@ function stageFiles(manifest, workspace) {
68208
69239
  const createdDirectories = [];
68209
69240
  try {
68210
69241
  for (const input of inputs) {
68211
- ensureStagingParent(root, dirname12(input.target), createdDirectories);
68212
- 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`);
68213
69244
  try {
68214
- const sourceStat = lstatSync12(input.source);
69245
+ const sourceStat = lstatSync13(input.source);
68215
69246
  cpSync(input.source, staged, {
68216
69247
  recursive: sourceStat.isDirectory(),
68217
69248
  dereference: false,
@@ -68223,14 +69254,14 @@ function stageFiles(manifest, workspace) {
68223
69254
  throw new Error(`benchmark input was not staged: ${input.file.destination}`);
68224
69255
  if (hashPath(staged) !== input.expectedHash)
68225
69256
  throw new Error(`benchmark input changed while staging: ${input.file.source}`);
68226
- if (lstatSync12(staged).isDirectory() && !listFiles2(staged).length)
69257
+ if (lstatSync13(staged).isDirectory() && !listFiles2(staged).length)
68227
69258
  throw new Error(`benchmark input staged an empty directory: ${input.file.destination}`);
68228
69259
  if (pathExists(input.target))
68229
69260
  throw new Error(`benchmark destinations must not overlap: ${input.file.destination}`);
68230
- renameSync8(staged, input.target);
69261
+ renameSync9(staged, input.target);
68231
69262
  published.push(input.target);
68232
69263
  } finally {
68233
- rmSync10(staged, {
69264
+ rmSync11(staged, {
68234
69265
  recursive: true,
68235
69266
  force: true
68236
69267
  });
@@ -68238,7 +69269,7 @@ function stageFiles(manifest, workspace) {
68238
69269
  }
68239
69270
  } catch (error) {
68240
69271
  for (const target of published.reverse())
68241
- rmSync10(target, {
69272
+ rmSync11(target, {
68242
69273
  recursive: true,
68243
69274
  force: true
68244
69275
  });
@@ -68258,42 +69289,42 @@ function pathsOverlap(left, right) {
68258
69289
  return isDescendantPath(leftToRight) || isDescendantPath(rightToLeft);
68259
69290
  }
68260
69291
  function isDescendantPath(value) {
68261
- return Boolean(value) && value !== ".." && !value.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute10(value);
69292
+ return Boolean(value) && value !== ".." && !value.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute11(value);
68262
69293
  }
68263
69294
  function listFiles2(rootPath) {
68264
- if (!existsSync26(rootPath))
69295
+ if (!existsSync29(rootPath))
68265
69296
  return [];
68266
- const stat = lstatSync12(rootPath);
69297
+ const stat = lstatSync13(rootPath);
68267
69298
  if (stat.isFile())
68268
69299
  return [rootPath];
68269
69300
  if (!stat.isDirectory())
68270
69301
  throw new Error(`unsupported staged benchmark input type: ${rootPath}`);
68271
- return readdirSync13(rootPath).flatMap((name) => listFiles2(join35(rootPath, name)));
69302
+ return readdirSync14(rootPath).flatMap((name) => listFiles2(join37(rootPath, name)));
68272
69303
  }
68273
69304
  function ensureStagingParent(workspace, parent, createdDirectories) {
68274
- const root = resolve13(workspace);
68275
- const local = relative13(root, resolve13(parent));
69305
+ const root = resolve14(workspace);
69306
+ const local = relative13(root, resolve14(parent));
68276
69307
  if (local === "")
68277
69308
  return;
68278
69309
  if (!isDescendantPath(local))
68279
69310
  throw new Error("benchmark staging parent escapes scratch workspace");
68280
69311
  let current = root;
68281
69312
  for (const segment of local.split(/[\\/]+/).filter(Boolean)) {
68282
- current = join35(current, segment);
69313
+ current = join37(current, segment);
68283
69314
  if (!pathExists(current)) {
68284
- mkdirSync12(current, {
69315
+ mkdirSync13(current, {
68285
69316
  mode: 448
68286
69317
  });
68287
69318
  createdDirectories.push(current);
68288
69319
  }
68289
- const stat = lstatSync12(current);
69320
+ const stat = lstatSync13(current);
68290
69321
  if (!stat.isDirectory() || stat.isSymbolicLink())
68291
69322
  throw new Error(`benchmark staging parent must be a real directory: ${current}`);
68292
69323
  }
68293
69324
  }
68294
69325
  function pathExists(path) {
68295
69326
  try {
68296
- lstatSync12(path);
69327
+ lstatSync13(path);
68297
69328
  return true;
68298
69329
  } catch (error) {
68299
69330
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
@@ -68343,7 +69374,7 @@ class BenchmarkHostBackend {
68343
69374
  hostPath(path) {
68344
69375
  if (path === "/workspace")
68345
69376
  return this.workspace;
68346
- return join35(this.workspace, path.slice("/workspace/".length));
69377
+ return join37(this.workspace, path.slice("/workspace/".length));
68347
69378
  }
68348
69379
  }
68349
69380
  async function freezeRun(manifest, session, tools, faraiRoot, provider, kaliImageId) {
@@ -68491,7 +69522,7 @@ async function validateCandidates(manifest, sources, workspace) {
68491
69522
  }
68492
69523
  async function runOracle(oracle, candidate2, workspace, challengeId) {
68493
69524
  if (oracle.executableSha256) {
68494
- if (!existsSync26(oracle.command[0]))
69525
+ if (!existsSync29(oracle.command[0]))
68495
69526
  return {
68496
69527
  ok: false,
68497
69528
  error: "oracle executable missing"
@@ -68588,7 +69619,7 @@ var init_runner = __esm(() => {
68588
69619
  init_docker_lifecycle();
68589
69620
  init_hash();
68590
69621
  init_git_state();
68591
- init_manifest();
69622
+ init_manifest2();
68592
69623
  });
68593
69624
 
68594
69625
  // src/agent-benchmark/suite.ts
@@ -68599,16 +69630,16 @@ __export(exports_suite, {
68599
69630
  normalizeBenchmarkSuiteManifest: () => normalizeBenchmarkSuiteManifest,
68600
69631
  loadBenchmarkSuiteManifest: () => loadBenchmarkSuiteManifest
68601
69632
  });
68602
- import { mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4 } from "fs";
69633
+ import { mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync4 } from "fs";
68603
69634
  import { tmpdir as tmpdir5 } from "os";
68604
- import { join as join36 } from "path";
69635
+ import { join as join38 } from "path";
68605
69636
  async function runBenchmarkSuite(input, options = {}) {
68606
69637
  const manifest = normalizeBenchmarkSuiteManifest(input);
68607
69638
  const campaignId = id();
68608
- const root = options.artifactsDir ?? mkdtempSync4(join36(tmpdir5(), "farai-benchmark-campaign-"));
68609
- const bundlePath = join36(root, `${safeName3(manifest.id)}-${campaignId}`);
68610
- const runsPath = join36(bundlePath, "runs");
68611
- 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, {
68612
69643
  recursive: true
68613
69644
  });
68614
69645
  const attempts = [];
@@ -68701,9 +69732,9 @@ async function runBenchmarkSuite(input, options = {}) {
68701
69732
  error: outcome.error
68702
69733
  })
68703
69734
  };
68704
- atomicWriteFile(join36(bundlePath, "campaign.json"), `${JSON.stringify(result, null, 2)}
69735
+ atomicWriteFile(join38(bundlePath, "campaign.json"), `${JSON.stringify(result, null, 2)}
68705
69736
  `, 384);
68706
- atomicWriteFile(join36(bundlePath, "suite.sha256"), `${result.manifestHash}
69737
+ atomicWriteFile(join38(bundlePath, "suite.sha256"), `${result.manifestHash}
68707
69738
  `, 384);
68708
69739
  return result;
68709
69740
  }
@@ -68744,7 +69775,7 @@ function safeName3(value) {
68744
69775
  var init_suite = __esm(() => {
68745
69776
  init_atomic_file();
68746
69777
  init_hash();
68747
- init_manifest();
69778
+ init_manifest2();
68748
69779
  init_runner();
68749
69780
  });
68750
69781
 
@@ -68759,6 +69790,7 @@ init_global_config();
68759
69790
  init_config();
68760
69791
  init_branding();
68761
69792
  init_version();
69793
+ init_session_catalog();
68762
69794
 
68763
69795
  // src/cli/command-arguments.ts
68764
69796
  init_model_provider_validation();
@@ -68767,6 +69799,16 @@ function parseNoArguments(command, args) {
68767
69799
  if (args.length > 0)
68768
69800
  throw new Error(`${command} does not accept arguments`);
68769
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
+ }
68770
69812
  function parseSetupArguments(args) {
68771
69813
  const {
68772
69814
  values,
@@ -69266,6 +70308,13 @@ async function main() {
69266
70308
  ensureDefaultUserConfig();
69267
70309
  console.log(globalConfigPath());
69268
70310
  break;
70311
+ case "update":
70312
+ if (wantsHelp(args2)) {
70313
+ help("update");
70314
+ break;
70315
+ }
70316
+ await updateContent(args2);
70317
+ break;
69269
70318
  default:
69270
70319
  console.error(`unknown command: ${command}`);
69271
70320
  help();
@@ -69297,6 +70346,11 @@ async function doctor() {
69297
70346
  console.log(`kali image: ${backend2.image} (${image.exists ? "exists" : "missing"})`);
69298
70347
  console.log(`kali contract: ${image.contract ?? "missing"}`);
69299
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"}`);
69300
70354
  console.log(`setup command: farai setup`);
69301
70355
  }
69302
70356
  async function setup(args2) {
@@ -69437,6 +70491,14 @@ async function initLab(args2) {
69437
70491
  }
69438
70492
  async function launchTui(workspace, sessionId) {
69439
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
+ }
69440
70502
  if (import.meta.path.endsWith(".ts")) {
69441
70503
  const sourceTuiPreload = "@opentui/solid/preload";
69442
70504
  await import(sourceTuiPreload);
@@ -69454,6 +70516,13 @@ async function launchTui(workspace, sessionId) {
69454
70516
  process.exitCode = 1;
69455
70517
  }
69456
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
+ }
69457
70526
  async function run2(args2) {
69458
70527
  const {
69459
70528
  sessionId,
@@ -69619,7 +70688,14 @@ Usage:
69619
70688
  config: `Farai config
69620
70689
 
69621
70690
  Usage:
69622
- 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`
69623
70699
  };
69624
70700
  if (topic && !pages[topic])
69625
70701
  throw new Error(`unknown help topic: ${topic}`);
@@ -69637,6 +70713,7 @@ Usage:
69637
70713
  farai bench run <manifest.json> [--output result.json] [--workspace scratch-dir] [--artifacts dir]
69638
70714
  farai bench suite <suite.json> [--artifacts dir]
69639
70715
  farai config
70716
+ farai update [status|check|apply|rollback]
69640
70717
 
69641
70718
  settings live in ~/.local/pajarori/farai/config.toml; credentials use the system keyring.
69642
70719
 
@@ -69650,5 +70727,5 @@ Examples:
69650
70727
  `);
69651
70728
  }
69652
70729
 
69653
- //# debugId=37901988F279345364756E2164756E21
70730
+ //# debugId=59D0DF40CE3D0BDB64756E2164756E21
69654
70731
  //# sourceMappingURL=index.js.map