@gendigital/sage-openclaw 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,7 +67,7 @@ function Pn(s3, t) {
67
67
  function zn(s3, t) {
68
68
  s3.head = new ue(t, void 0, s3.head, s3), s3.tail || (s3.tail = s3.head), s3.length++;
69
69
  }
70
- var import_events, import_fs, import_node_events, import_node_stream, import_node_string_decoder, import_node_path4, import_node_fs2, import_path, import_events2, import_assert, import_buffer, ks, import_zlib, import_node_path5, import_node_path6, import_fs2, import_fs3, import_path2, import_node_path7, import_path3, import_node_fs3, import_node_assert, import_node_crypto2, import_node_fs4, import_node_path8, import_fs4, import_node_fs5, import_node_path9, import_node_fs6, import_promises2, import_node_path10, import_node_path11, import_node_fs7, import_node_path12, kr, vr, Os, Br, Pr, zr, q, j, rt, Le, jt, Ne, Ts, Ae, xs, z, Mt, b, Qt, Bt, _, A, g, yi, De, L, w, Ri, bi, Ls, _i, Z, gi, Ie, Jt, yt, C, te, Ur, Hr, Wr, Gr, Ce, Oi, Zr, Yr, D, Vr, ot, H, ee, m, xi, J, Li, Ii, Ci, se, Fe, Ut, Ht, Ni, Pt, ht, U, nt, Y, zt, Ai, Q, ie, Di, ke, Rt, ve, bt, _t, Me, tt, Wt, $r, As, Ds, Is, Cs, Fs, Xr, re, K, jr, M, Qr, vs, Jr, ki, Ot, Gt, vi, ne, Be, Pe, ze, Ue, He, We, Ge, Ze, Ye, Ms, en, sn, Bs, rn, nn, Ps, zs, Bi, oe, hn, he, Ke, F, an, Tt, Pi, ln, at, cn, fn, dn, lt, un, mn, pn, zi, En, xt, ct, Sn, yn, Rn, bn, f, Yt, Lt, gn, Zi, Yi, On, B, Nt, et, Ui, Us, V, ae, ft, Hs, p, it, dt, Hi, At, y, Ve, $e, Wi, Ws, Gs, le, Gi, Xe, Kt, ut, qe, Dt, je, Qe, Zs, Tn, st, mt, Nn, Ki, An, Dn, It, Vi, Cn, Ys, ce, Je, $i, Fn, kn, Xi, Ks, Js, vn, Xs, qs, js, ji, Qs, fe, ti, Qi, ei, Ji, ts, es, is, pt, ii, ss, qi, X, de, si, ri, Mn, ni, ue, di, tr, oi, me, W, Ct, Ft, pe, rs, G, ns, hi, er, as, ls, ai, li, ir, os, ci, rr, hs, Et, kt, Un, Hn, or, hr, Wn, Gn, Zn, Yn, fr, dr, ar, ur, mr, pr, Kn, Vn, $n, lr, cs, fs2, ui, Xn, ds, qn, us, we, wt, Qn, Er, ms, wr, Jn, Sr, ps, yr, $t, Rr, to, eo, io, Ei, _r, gr, ys, Or, Rs, P, bs, _s, Si, Tr, xr, ye, Lr, Nr, Es, St, O, wi, Ar, Xt, ws, Ss, gs, Re, be, _e, ge, ro, Oe, no, oo, ho, Dr, qt, Se, Te, ao, lo, co, fo, uo, mo, po, Eo, vt, wo, So;
70
+ var import_events, import_fs, import_node_events, import_node_stream, import_node_string_decoder, import_node_path4, import_node_fs2, import_path, import_events2, import_assert, import_buffer, ks, import_zlib, import_node_path5, import_node_path6, import_fs2, import_fs3, import_path2, import_node_path7, import_path3, import_node_fs3, import_node_assert, import_node_crypto2, import_node_fs4, import_node_path8, import_fs4, import_node_fs5, import_node_path9, import_node_fs6, import_promises3, import_node_path10, import_node_path11, import_node_fs7, import_node_path12, kr, vr, Os, Br, Pr, zr, q, j, rt, Le, jt, Ne, Ts, Ae, xs, z, Mt, b, Qt, Bt, _, A, g, yi, De, L, w, Ri, bi, Ls, _i, Z, gi, Ie, Jt, yt, C, te, Ur, Hr, Wr, Gr, Ce, Oi, Zr, Yr, D, Vr, ot, H, ee, m, xi, J, Li, Ii, Ci, se, Fe, Ut, Ht, Ni, Pt, ht, U, nt, Y, zt, Ai, Q, ie, Di, ke, Rt, ve, bt, _t, Me, tt, Wt, $r, As, Ds, Is, Cs, Fs, Xr, re, K, jr, M, Qr, vs, Jr, ki, Ot, Gt, vi, ne, Be, Pe, ze, Ue, He, We, Ge, Ze, Ye, Ms, en, sn, Bs, rn, nn, Ps, zs, Bi, oe, hn, he, Ke, F, an, Tt, Pi, ln, at, cn, fn, dn, lt, un, mn, pn, zi, En, xt, ct, Sn, yn, Rn, bn, f, Yt, Lt, gn, Zi, Yi, On, B, Nt, et, Ui, Us, V, ae, ft, Hs, p, it, dt, Hi, At, y, Ve, $e, Wi, Ws, Gs, le, Gi, Xe, Kt, ut, qe, Dt, je, Qe, Zs, Tn, st, mt, Nn, Ki, An, Dn, It, Vi, Cn, Ys, ce, Je, $i, Fn, kn, Xi, Ks, Js, vn, Xs, qs, js, ji, Qs, fe, ti, Qi, ei, Ji, ts, es, is, pt, ii, ss, qi, X, de, si, ri, Mn, ni, ue, di, tr, oi, me, W, Ct, Ft, pe, rs, G, ns, hi, er, as, ls, ai, li, ir, os, ci, rr, hs, Et, kt, Un, Hn, or, hr, Wn, Gn, Zn, Yn, fr, dr, ar, ur, mr, pr, Kn, Vn, $n, lr, cs, fs2, ui, Xn, ds, qn, us, we, wt, Qn, Er, ms, wr, Jn, Sr, ps, yr, $t, Rr, to, eo, io, Ei, _r, gr, ys, Or, Rs, P, bs, _s, Si, Tr, xr, ye, Lr, Nr, Es, St, O, wi, Ar, Xt, ws, Ss, gs, Re, be, _e, ge, ro, Oe, no, oo, ho, Dr, qt, Se, Te, ao, lo, co, fo, uo, mo, po, Eo, vt, wo, So;
71
71
  var init_index_min = __esm({
72
72
  "../../node_modules/.pnpm/tar@7.5.13/node_modules/tar/dist/esm/index.min.js"() {
73
73
  import_events = __toESM(require("events"), 1);
@@ -99,7 +99,7 @@ var init_index_min = __esm({
99
99
  import_node_fs5 = __toESM(require("node:fs"), 1);
100
100
  import_node_path9 = __toESM(require("node:path"), 1);
101
101
  import_node_fs6 = __toESM(require("node:fs"), 1);
102
- import_promises2 = __toESM(require("node:fs/promises"), 1);
102
+ import_promises3 = __toESM(require("node:fs/promises"), 1);
103
103
  import_node_path10 = __toESM(require("node:path"), 1);
104
104
  import_node_path11 = require("node:path");
105
105
  import_node_fs7 = __toESM(require("node:fs"), 1);
@@ -2258,7 +2258,7 @@ var init_index_min = __esm({
2258
2258
  E ? e(E) : x && a ? ds(x, o, h, (xe) => S(xe)) : n ? import_node_fs6.default.chmod(s3, r, e) : e();
2259
2259
  };
2260
2260
  if (s3 === d) return Qn(s3, S);
2261
- if (l) return import_promises2.default.mkdir(s3, { mode: r, recursive: true }).then((E) => S(null, E ?? void 0), S);
2261
+ if (l) return import_promises3.default.mkdir(s3, { mode: r, recursive: true }).then((E) => S(null, E ?? void 0), S);
2262
2262
  let N = f(import_node_path10.default.relative(d, s3)).split("/");
2263
2263
  ms(d, N, r, c, d, void 0, S);
2264
2264
  };
@@ -2970,6 +2970,7 @@ var import_node_path = require("node:path");
2970
2970
  var fs = __toESM(require("node:fs"), 1);
2971
2971
  var fsPromises = __toESM(require("node:fs/promises"), 1);
2972
2972
  var import_node_os = require("node:os");
2973
+ var import_promises = require("node:timers/promises");
2973
2974
  var name1 = "read";
2974
2975
  var name2 = "File";
2975
2976
  function getFileContent(path, encoding = "utf-8") {
@@ -2984,6 +2985,43 @@ function getProcEnv() {
2984
2985
  function getHomeDir() {
2985
2986
  return getProcEnv().HOME || (0, import_node_os.homedir)();
2986
2987
  }
2988
+ async function removeStaleFileLock(lockPath, staleAgeMs) {
2989
+ try {
2990
+ const s3 = await fsPromises.stat(lockPath);
2991
+ if (Date.now() - s3.mtimeMs < staleAgeMs) return;
2992
+ await fsPromises.rmdir(lockPath);
2993
+ } catch {
2994
+ }
2995
+ }
2996
+ async function acquireFileLock(filePath, timeoutMs = 250, staleAgeMs = 3e4) {
2997
+ const lockPath = `${filePath}.lock`;
2998
+ const deadline = Date.now() + timeoutMs;
2999
+ while (true) {
3000
+ try {
3001
+ await fsPromises.mkdir(lockPath);
3002
+ return async () => {
3003
+ try {
3004
+ await fsPromises.rmdir(lockPath);
3005
+ } catch {
3006
+ }
3007
+ };
3008
+ } catch {
3009
+ await removeStaleFileLock(lockPath, staleAgeMs);
3010
+ const remainingMs = deadline - Date.now();
3011
+ if (remainingMs <= 0) return void 0;
3012
+ await (0, import_promises.setTimeout)(Math.min(50, remainingMs));
3013
+ }
3014
+ }
3015
+ }
3016
+ async function withFileLock(filePath, callback) {
3017
+ const release = await acquireFileLock(filePath);
3018
+ if (!release) return;
3019
+ try {
3020
+ return await callback();
3021
+ } finally {
3022
+ await release();
3023
+ }
3024
+ }
2987
3025
 
2988
3026
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
2989
3027
  var external_exports = {};
@@ -7099,14 +7137,21 @@ var PackageCheckConfigSchema = external_exports.object({
7099
7137
  var AmsiCheckConfigSchema = external_exports.object({
7100
7138
  enabled: external_exports.boolean().default(true)
7101
7139
  });
7102
- var DEFAULT_PI_HIGH_RISK_THRESHOLD = 0.99;
7103
- var DEFAULT_PI_MEDIUM_RISK_THRESHOLD = 0.5;
7140
+ var SkillCheckConfigSchema = external_exports.object({
7141
+ enabled: external_exports.boolean().default(true),
7142
+ cache_ttl_days: external_exports.number().min(0).default(1),
7143
+ /**
7144
+ * Upload unknown skills (never seen by the analyzer) for deep content
7145
+ * analysis. When false, the scan still looks skills up by content hash and
7146
+ * still flags known-risky ones — but no skill content ever leaves the
7147
+ * machine and the upload worker never runs (lookup-only mode).
7148
+ */
7149
+ upload_enabled: external_exports.boolean().default(true)
7150
+ });
7104
7151
  var PiCheckConfigSchema = external_exports.object({
7105
7152
  enabled: external_exports.boolean().default(false),
7106
7153
  max_content_length: external_exports.number().default(16384),
7107
- model_path: external_exports.string().optional(),
7108
- high_risk_threshold: external_exports.number().default(DEFAULT_PI_HIGH_RISK_THRESHOLD),
7109
- medium_risk_threshold: external_exports.number().default(DEFAULT_PI_MEDIUM_RISK_THRESHOLD)
7154
+ model_path: external_exports.string().optional()
7110
7155
  });
7111
7156
  var ExceptionDecisionSchema = external_exports.enum(["allow", "deny"]);
7112
7157
  var ExceptionMatchSchema = external_exports.enum(["executable", "domain", "path", "plugin", "regex"]);
@@ -7128,6 +7173,7 @@ var ConfigSchema = external_exports.object({
7128
7173
  file_check: FileCheckConfigSchema.default({}),
7129
7174
  package_check: PackageCheckConfigSchema.default({}),
7130
7175
  amsi_check: AmsiCheckConfigSchema.default({}),
7176
+ skill_check: SkillCheckConfigSchema.default({}),
7131
7177
  pi_check: PiCheckConfigSchema.default({}),
7132
7178
  heuristics_enabled: external_exports.boolean().default(true),
7133
7179
  cache: CacheConfigSchema.default({}),
@@ -7136,6 +7182,7 @@ var ConfigSchema = external_exports.object({
7136
7182
  operational_logging: OperationalLoggingConfigSchema.default({}),
7137
7183
  sensitivity: SensitivitySchema.default("balanced"),
7138
7184
  disabled_threats: external_exports.array(external_exports.string()).default([]),
7185
+ announce_clean_scans: external_exports.boolean().default(true),
7139
7186
  brand_key: external_exports.string().min(1).max(32).regex(/^[a-z0-9_-]+$/u).optional(),
7140
7187
  community_iq: external_exports.boolean().default(true)
7141
7188
  });
@@ -7150,6 +7197,8 @@ var HookTypeSchema = external_exports.enum([
7150
7197
 
7151
7198
  // ../core/src/config.ts
7152
7199
  var SAGE_DIR = "~/.sage";
7200
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
7201
+ var CLOCK_SKEW_TOLERANCE_MS = 5 * 60 * 1e3;
7153
7202
  function resolvedSageDir() {
7154
7203
  return resolvePath(SAGE_DIR);
7155
7204
  }
@@ -7216,7 +7265,7 @@ function sanitizeBrandKey(data, logger) {
7216
7265
  if (typeof brandKey === "string" && brandKey.length >= 1 && brandKey.length <= 32 && BRAND_KEY_RE.test(brandKey)) {
7217
7266
  return data;
7218
7267
  }
7219
- logger.warn(`Invalid brand_key in config \u2014 ignoring`, { brand_key: brandKey });
7268
+ logger.warn(`Invalid brand_key in config - ignoring`, { brand_key: brandKey });
7220
7269
  const { brand_key: _2, ...rest } = data;
7221
7270
  return rest;
7222
7271
  }
@@ -7284,7 +7333,7 @@ async function loadConfig(configPath, logger = nullLogger) {
7284
7333
 
7285
7334
  // ../core/src/installation-id.ts
7286
7335
  var import_node_crypto = require("node:crypto");
7287
- var import_promises = require("node:fs/promises");
7336
+ var import_promises2 = require("node:fs/promises");
7288
7337
  var import_node_path2 = require("node:path");
7289
7338
  async function getInstallationId(sageDirPath) {
7290
7339
  const sageDir = sageDirPath ?? resolvePath("~/.sage");
@@ -7299,8 +7348,8 @@ async function getInstallationId(sageDirPath) {
7299
7348
  }
7300
7349
  try {
7301
7350
  const id = (0, import_node_crypto.randomUUID)();
7302
- await (0, import_promises.mkdir)(sageDir, { recursive: true, mode: 448 });
7303
- await (0, import_promises.writeFile)(idPath, id, { encoding: "utf-8", mode: 384, flag: fileExists ? "w" : "wx" });
7351
+ await (0, import_promises2.mkdir)(sageDir, { recursive: true, mode: 448 });
7352
+ await (0, import_promises2.writeFile)(idPath, id, { encoding: "utf-8", mode: 384, flag: fileExists ? "w" : "wx" });
7304
7353
  return id;
7305
7354
  } catch (err) {
7306
7355
  if (err.code === "EEXIST") {
@@ -7318,17 +7367,18 @@ async function getInstallationId(sageDirPath) {
7318
7367
  // ../core/src/clients/model-downloader.ts
7319
7368
  var import_node_crypto3 = require("node:crypto");
7320
7369
  var import_node_fs8 = require("node:fs");
7321
- var import_promises3 = require("node:fs/promises");
7370
+ var import_promises4 = require("node:fs/promises");
7322
7371
  var import_node_path13 = require("node:path");
7323
7372
  var import_node_stream2 = require("node:stream");
7324
- var import_promises4 = require("node:stream/promises");
7373
+ var import_promises5 = require("node:stream/promises");
7325
7374
 
7326
7375
  // ../core/src/model-storage.ts
7327
7376
  var import_node_fs = require("node:fs");
7328
7377
  var import_node_path3 = require("node:path");
7329
- var MODEL_SCHEMA_VERSION = "v1";
7378
+ var MODEL_SCHEMA_VERSION = "v2";
7330
7379
  var REQUIRED_MODELS_BY_SCHEMA = {
7331
- v1: ["pi-model"]
7380
+ v1: ["pi-model"],
7381
+ v2: ["pi-model"]
7332
7382
  };
7333
7383
  var REQUIRED_MODEL_FILES = {
7334
7384
  "pi-model": [
@@ -7377,8 +7427,8 @@ async function downloadModel(opts) {
7377
7427
  const stagingDir = getDownloadStagingDir(sageDir);
7378
7428
  const lockPath = `${modelDir}.lock`;
7379
7429
  try {
7380
- await (0, import_promises3.mkdir)(stagingDir, { recursive: true });
7381
- await (0, import_promises3.mkdir)((0, import_node_path13.resolve)(modelDir, ".."), { recursive: true });
7430
+ await (0, import_promises4.mkdir)(stagingDir, { recursive: true });
7431
+ await (0, import_promises4.mkdir)((0, import_node_path13.resolve)(modelDir, ".."), { recursive: true });
7382
7432
  } catch (err) {
7383
7433
  logger.warn(`failed to prepare model storage for ${modelName}: ${err}`);
7384
7434
  return false;
@@ -7404,7 +7454,7 @@ async function downloadModel(opts) {
7404
7454
  });
7405
7455
  return false;
7406
7456
  }
7407
- await (0, import_promises3.mkdir)(extractDir, { recursive: true });
7457
+ await (0, import_promises4.mkdir)(extractDir, { recursive: true });
7408
7458
  await extractTarGz(archivePath, extractDir);
7409
7459
  const extractDirResolved = extractDir;
7410
7460
  const missing = requiredModelFiles(modelName).filter(
@@ -7417,26 +7467,26 @@ async function downloadModel(opts) {
7417
7467
  const aside = `${modelDir}.${suffix}.old`;
7418
7468
  let asideUsed = false;
7419
7469
  if (await pathExists(modelDir)) {
7420
- await (0, import_promises3.rename)(modelDir, aside);
7470
+ await (0, import_promises4.rename)(modelDir, aside);
7421
7471
  asideUsed = true;
7422
7472
  }
7423
7473
  try {
7424
- await (0, import_promises3.rename)(extractDir, modelDir);
7474
+ await (0, import_promises4.rename)(extractDir, modelDir);
7425
7475
  extractDir = null;
7426
7476
  } catch (err) {
7427
7477
  if (asideUsed) {
7428
7478
  try {
7429
- await (0, import_promises3.rename)(aside, modelDir);
7479
+ await (0, import_promises4.rename)(aside, modelDir);
7430
7480
  } catch {
7431
7481
  }
7432
7482
  }
7433
7483
  throw err;
7434
7484
  }
7435
7485
  if (asideUsed) {
7436
- await (0, import_promises3.rm)(aside, { recursive: true, force: true }).catch(() => {
7486
+ await (0, import_promises4.rm)(aside, { recursive: true, force: true }).catch(() => {
7437
7487
  });
7438
7488
  }
7439
- await (0, import_promises3.unlink)(archivePath).catch(() => {
7489
+ await (0, import_promises4.unlink)(archivePath).catch(() => {
7440
7490
  });
7441
7491
  archivePath = null;
7442
7492
  logger.info(`model ${modelName} installed (schema ${schema})`);
@@ -7448,14 +7498,14 @@ async function downloadModel(opts) {
7448
7498
  return false;
7449
7499
  } finally {
7450
7500
  if (extractDir) {
7451
- await (0, import_promises3.rm)(extractDir, { recursive: true, force: true }).catch(() => {
7501
+ await (0, import_promises4.rm)(extractDir, { recursive: true, force: true }).catch(() => {
7452
7502
  });
7453
7503
  }
7454
7504
  if (archivePath) {
7455
- await (0, import_promises3.unlink)(archivePath).catch(() => {
7505
+ await (0, import_promises4.unlink)(archivePath).catch(() => {
7456
7506
  });
7457
7507
  }
7458
- await (0, import_promises3.unlink)(lockPath).catch(() => {
7508
+ await (0, import_promises4.unlink)(lockPath).catch(() => {
7459
7509
  });
7460
7510
  }
7461
7511
  }
@@ -7468,11 +7518,11 @@ async function streamFetchToFile(url, destPath, fetchImpl = fetch) {
7468
7518
  throw new Error(`empty response body from ${url}`);
7469
7519
  }
7470
7520
  const nodeStream = import_node_stream2.Readable.fromWeb(response.body);
7471
- await (0, import_promises4.pipeline)(nodeStream, (0, import_node_fs8.createWriteStream)(destPath));
7521
+ await (0, import_promises5.pipeline)(nodeStream, (0, import_node_fs8.createWriteStream)(destPath));
7472
7522
  }
7473
7523
  async function sha256OfFile(path) {
7474
7524
  const hash = (0, import_node_crypto3.createHash)("sha256");
7475
- const buf = await (0, import_promises3.readFile)(path);
7525
+ const buf = await (0, import_promises4.readFile)(path);
7476
7526
  hash.update(buf);
7477
7527
  return hash.digest("hex");
7478
7528
  }
@@ -7486,7 +7536,7 @@ async function extractTarGz(archivePath, destDir) {
7486
7536
  }
7487
7537
  async function pathExists(path) {
7488
7538
  try {
7489
- await (0, import_promises3.stat)(path);
7539
+ await (0, import_promises4.stat)(path);
7490
7540
  return true;
7491
7541
  } catch {
7492
7542
  return false;
@@ -7503,7 +7553,7 @@ function fileExistsSync(path) {
7503
7553
  async function tryAcquireLock(lockPath, logger) {
7504
7554
  try {
7505
7555
  const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
7506
- await (0, import_promises3.writeFile)(lockPath, payload, { flag: "wx" });
7556
+ await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
7507
7557
  return true;
7508
7558
  } catch (err) {
7509
7559
  const code = err.code;
@@ -7513,15 +7563,15 @@ async function tryAcquireLock(lockPath, logger) {
7513
7563
  }
7514
7564
  }
7515
7565
  try {
7516
- const raw = await (0, import_promises3.readFile)(lockPath, "utf-8");
7566
+ const raw = await (0, import_promises4.readFile)(lockPath, "utf-8");
7517
7567
  const parsed = JSON.parse(raw);
7518
7568
  const age = Date.now() - (parsed.ts ?? 0);
7519
7569
  if (age > STALE_LOCK_MS) {
7520
- await (0, import_promises3.unlink)(lockPath).catch(() => {
7570
+ await (0, import_promises4.unlink)(lockPath).catch(() => {
7521
7571
  });
7522
7572
  try {
7523
7573
  const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
7524
- await (0, import_promises3.writeFile)(lockPath, payload, { flag: "wx" });
7574
+ await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
7525
7575
  return true;
7526
7576
  } catch {
7527
7577
  return false;
@@ -7533,7 +7583,7 @@ async function tryAcquireLock(lockPath, logger) {
7533
7583
  }
7534
7584
 
7535
7585
  // ../core/src/sage-proxy.ts
7536
- function mapSageProxyOs(platform) {
7586
+ function mapSageHostOs(platform) {
7537
7587
  switch (platform) {
7538
7588
  case "win32":
7539
7589
  return "WINDOWS";
@@ -7545,21 +7595,37 @@ function mapSageProxyOs(platform) {
7545
7595
  return platform;
7546
7596
  }
7547
7597
  }
7548
- function mapSageProxyArchitecture(arch) {
7598
+ function mapSageHostArchitecture(arch) {
7549
7599
  return arch.toUpperCase();
7550
7600
  }
7601
+ function buildSageUserConfig(config) {
7602
+ return {
7603
+ sensitivity: config.sensitivity,
7604
+ url_check_enabled: config.url_check.enabled,
7605
+ file_check_enabled: config.file_check.enabled,
7606
+ package_check_enabled: config.package_check.enabled,
7607
+ heuristics_enabled: config.heuristics_enabled,
7608
+ pi_check_enabled: config.pi_check.enabled,
7609
+ community_iq_enabled: config.community_iq,
7610
+ // Optional-chained + defaulted: real callers pass a loadConfig result (skill_check
7611
+ // always present via the schema default), but this feeds fail-open telemetry, so a
7612
+ // partial config must never crash the envelope build.
7613
+ skill_check_upload_enabled: config.skill_check?.upload_enabled ?? true
7614
+ };
7615
+ }
7551
7616
  function buildSageProxyEnvelope(args) {
7552
7617
  return {
7553
7618
  identity: { uuid: args.iid },
7554
7619
  product: { version_app: args.versionApp },
7555
7620
  platform: {
7556
- os: args.platformOs ?? mapSageProxyOs(process.platform),
7557
- architecture: args.platformArchitecture ?? mapSageProxyArchitecture(process.arch)
7621
+ os: args.platformOs ?? mapSageHostOs(process.platform),
7622
+ architecture: args.platformArchitecture ?? mapSageHostArchitecture(process.arch)
7558
7623
  },
7559
7624
  agent: {
7560
7625
  agent_runtime: args.agentRuntime,
7561
7626
  agent_runtime_version: args.agentRuntimeVersion
7562
- }
7627
+ },
7628
+ ...args.config ? { config: buildSageUserConfig(args.config) } : {}
7563
7629
  };
7564
7630
  }
7565
7631
 
@@ -7568,7 +7634,7 @@ var import_node_path14 = require("node:path");
7568
7634
  var import_node_url = require("node:url");
7569
7635
  var import_meta = {};
7570
7636
  function resolveVersion() {
7571
- if (true) return "0.11.0";
7637
+ if (true) return "0.12.0";
7572
7638
  try {
7573
7639
  const pkgPath = (0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
7574
7640
  const pkg = JSON.parse(getFileContentSync(pkgPath));
@@ -7607,14 +7673,23 @@ async function fetchModelManifest(args) {
7607
7673
  logger.debug("Skipping model manifest fetch: missing installation id");
7608
7674
  return null;
7609
7675
  }
7610
- const envelope = buildSageProxyEnvelope({
7611
- iid: args.iid,
7612
- versionApp: args.versionApp ?? VERSION,
7613
- agentRuntime: args.agentRuntime,
7614
- agentRuntimeVersion: args.agentRuntimeVersion ?? "unknown"
7615
- });
7616
- const body = { ...envelope, models: { schema: args.schema } };
7676
+ let config = args.config;
7677
+ if (!config) {
7678
+ try {
7679
+ config = await loadConfig(args.configPath, logger);
7680
+ } catch (err) {
7681
+ logger.debug(`Model manifest config load failed: ${err}`);
7682
+ }
7683
+ }
7617
7684
  try {
7685
+ const envelope = buildSageProxyEnvelope({
7686
+ iid: args.iid,
7687
+ versionApp: args.versionApp ?? VERSION,
7688
+ agentRuntime: args.agentRuntime,
7689
+ agentRuntimeVersion: args.agentRuntimeVersion ?? "unknown",
7690
+ config
7691
+ });
7692
+ const body = { ...envelope, models: { schema: args.schema } };
7618
7693
  const response = await fetch(resolveEndpoint("/v2/model-manifest"), {
7619
7694
  method: "POST",
7620
7695
  signal: AbortSignal.timeout(timeoutMs),
@@ -7691,6 +7766,8 @@ async function ensureModelsAvailable(args) {
7691
7766
  schema,
7692
7767
  agentRuntime: args.agentRuntime ?? "unknown",
7693
7768
  agentRuntimeVersion: args.agentRuntimeVersion,
7769
+ config: args.config,
7770
+ configPath: args.configPath,
7694
7771
  versionApp: args.versionApp,
7695
7772
  logger
7696
7773
  });
@@ -7736,78 +7813,42 @@ async function ensureModelsAvailable(args) {
7736
7813
  }
7737
7814
 
7738
7815
  // ../core/src/jsonl-log-writer.ts
7739
- var import_promises5 = require("node:fs/promises");
7816
+ var import_promises6 = require("node:fs/promises");
7740
7817
  var import_node_path15 = require("node:path");
7741
- var import_promises6 = require("node:timers/promises");
7742
7818
  var writeQueues = /* @__PURE__ */ new Map();
7743
- var ROTATE_LOCK_TIMEOUT_MS = 250;
7744
- var ROTATE_LOCK_STALE_MS = 3e4;
7745
- var ROTATE_LOCK_POLL_MS = 50;
7746
7819
  async function shouldRotate(filePath, maxBytes, maxFiles) {
7747
7820
  if (maxBytes <= 0 || maxFiles <= 0) return false;
7748
7821
  try {
7749
- const s3 = await (0, import_promises5.stat)(filePath);
7822
+ const s3 = await (0, import_promises6.stat)(filePath);
7750
7823
  return s3.size >= maxBytes;
7751
7824
  } catch {
7752
7825
  return false;
7753
7826
  }
7754
7827
  }
7755
- async function removeStaleRotateLock(lockPath) {
7756
- try {
7757
- const s3 = await (0, import_promises5.stat)(lockPath);
7758
- if (Date.now() - s3.mtimeMs < ROTATE_LOCK_STALE_MS) return;
7759
- await (0, import_promises5.rmdir)(lockPath);
7760
- } catch {
7761
- }
7762
- }
7763
- async function acquireRotateLock(filePath) {
7764
- const lockPath = `${filePath}.lock`;
7765
- const deadline = Date.now() + ROTATE_LOCK_TIMEOUT_MS;
7766
- while (true) {
7767
- try {
7768
- await (0, import_promises5.mkdir)(lockPath);
7769
- return async () => {
7770
- try {
7771
- await (0, import_promises5.rmdir)(lockPath);
7772
- } catch {
7773
- }
7774
- };
7775
- } catch {
7776
- await removeStaleRotateLock(lockPath);
7777
- const remainingMs = deadline - Date.now();
7778
- if (remainingMs <= 0) return void 0;
7779
- await (0, import_promises6.setTimeout)(Math.min(ROTATE_LOCK_POLL_MS, remainingMs));
7780
- }
7781
- }
7782
- }
7783
7828
  async function rotateIfNeeded(filePath, maxBytes, maxFiles) {
7784
7829
  if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
7785
- const releaseLock = await acquireRotateLock(filePath);
7786
- if (!releaseLock) return;
7787
- try {
7830
+ await withFileLock(filePath, async () => {
7788
7831
  if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
7789
7832
  try {
7790
- await (0, import_promises5.unlink)(`${filePath}.${maxFiles}`);
7833
+ await (0, import_promises6.unlink)(`${filePath}.${maxFiles}`);
7791
7834
  } catch {
7792
7835
  }
7793
7836
  for (let i = maxFiles - 1; i >= 1; i--) {
7794
7837
  try {
7795
- await (0, import_promises5.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
7838
+ await (0, import_promises6.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
7796
7839
  } catch {
7797
7840
  }
7798
7841
  }
7799
7842
  try {
7800
- await (0, import_promises5.rename)(filePath, `${filePath}.1`);
7843
+ await (0, import_promises6.rename)(filePath, `${filePath}.1`);
7801
7844
  } catch {
7802
7845
  }
7803
- } finally {
7804
- await releaseLock();
7805
- }
7846
+ });
7806
7847
  }
7807
7848
  async function appendJsonlEntryNow(path, config, entry) {
7808
- await (0, import_promises5.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
7849
+ await (0, import_promises6.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
7809
7850
  await rotateIfNeeded(path, config.max_bytes, config.max_files);
7810
- await (0, import_promises5.appendFile)(path, `${JSON.stringify(entry)}
7851
+ await (0, import_promises6.appendFile)(path, `${JSON.stringify(entry)}
7811
7852
  `);
7812
7853
  }
7813
7854
  async function appendJsonlEntry(config, entry) {
@@ -7937,11 +7978,12 @@ function createOperationalLogger(config, runtime) {
7937
7978
  async function readWorkerArgs() {
7938
7979
  const env = process.env;
7939
7980
  const sageDir = env.SAGE_DIR ? resolvePath(env.SAGE_DIR) : resolvePath("~/.sage");
7981
+ const configPath = env.SAGE_CONFIG_PATH ? resolvePath(env.SAGE_CONFIG_PATH) : void 0;
7940
7982
  const agentRuntime = env.SAGE_AGENT_RUNTIME ?? "unknown";
7941
7983
  const agentRuntimeVersion = env.SAGE_AGENT_RUNTIME_VERSION || void 0;
7942
7984
  const versionApp = env.SAGE_VERSION_APP || void 0;
7943
7985
  const schema = env.SAGE_MODEL_SCHEMA || MODEL_SCHEMA_VERSION;
7944
- return { sageDir, agentRuntime, agentRuntimeVersion, versionApp, schema };
7986
+ return { sageDir, configPath, agentRuntime, agentRuntimeVersion, versionApp, schema };
7945
7987
  }
7946
7988
  async function createWorkerLogger(agentRuntime) {
7947
7989
  try {
@@ -7981,6 +8023,7 @@ async function workerMain() {
7981
8023
  iid,
7982
8024
  agentRuntime: args.agentRuntime,
7983
8025
  agentRuntimeVersion: args.agentRuntimeVersion,
8026
+ configPath: args.configPath,
7984
8027
  versionApp: args.versionApp,
7985
8028
  schema: args.schema,
7986
8029
  logger