@gendigital/sage-openclaw 0.11.0 → 0.13.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.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs +1648 -489
  3. package/dist/index.cjs.map +4 -4
  4. package/dist/mcp-server.cjs +230 -96
  5. package/dist/mcp-server.cjs.map +4 -4
  6. package/dist/model-download-worker.cjs +139 -94
  7. package/dist/model-download-worker.cjs.map +3 -3
  8. package/dist/skill-upload-worker.cjs +5638 -0
  9. package/dist/skill-upload-worker.cjs.map +7 -0
  10. package/openclaw.plugin.json +6 -1
  11. package/package.json +8 -3
  12. package/resources/threats/_macros.yaml +193 -0
  13. package/resources/threats/agent-layer.yaml +19 -1
  14. package/resources/threats/commands.yaml +165 -31
  15. package/resources/threats/credentials.yaml +24 -4
  16. package/resources/threats/dummy.yaml +52 -17
  17. package/resources/threats/files.yaml +38 -0
  18. package/resources/threats/mac-commands.yaml +118 -27
  19. package/resources/threats/mac-credentials.yaml +34 -8
  20. package/resources/threats/mac-files.yaml +20 -0
  21. package/resources/threats/mac-obfuscation.yaml +28 -6
  22. package/resources/threats/mac-persistence.yaml +30 -4
  23. package/resources/threats/mac-supply-chain.yaml +12 -3
  24. package/resources/threats/mitre.yaml +325 -62
  25. package/resources/threats/obfuscation.yaml +33 -8
  26. package/resources/threats/persistence.yaml +40 -5
  27. package/resources/threats/prompt-injection.yaml +58 -0
  28. package/resources/threats/self-defense.yaml +15 -2
  29. package/resources/threats/supply_chain.yaml +6 -1
  30. package/resources/threats/urls.yaml +10 -0
  31. package/resources/threats/win-commands.yaml +294 -60
  32. package/resources/threats/win-credentials.yaml +68 -9
  33. package/resources/threats/win-files.yaml +20 -0
  34. package/resources/threats/win-obfuscation.yaml +56 -8
  35. package/resources/threats/win-persistence.yaml +45 -7
  36. package/resources/threats/win-supply-chain.yaml +5 -1
@@ -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 = {};
@@ -7046,7 +7084,8 @@ var ArtifactSchema = external_exports.object({
7046
7084
  var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
7047
7085
  var ThreatSchema = external_exports.object({
7048
7086
  id: external_exports.string(),
7049
- version: external_exports.number().int().optional(),
7087
+ version: external_exports.number().int().positive(),
7088
+ detectionName: external_exports.string(),
7050
7089
  category: external_exports.string(),
7051
7090
  severity: VerdictSeveritySchema,
7052
7091
  confidence: external_exports.number(),
@@ -7099,14 +7138,21 @@ var PackageCheckConfigSchema = external_exports.object({
7099
7138
  var AmsiCheckConfigSchema = external_exports.object({
7100
7139
  enabled: external_exports.boolean().default(true)
7101
7140
  });
7102
- var DEFAULT_PI_HIGH_RISK_THRESHOLD = 0.99;
7103
- var DEFAULT_PI_MEDIUM_RISK_THRESHOLD = 0.5;
7141
+ var SkillCheckConfigSchema = external_exports.object({
7142
+ enabled: external_exports.boolean().default(true),
7143
+ cache_ttl_days: external_exports.number().min(0).default(1),
7144
+ /**
7145
+ * Upload unknown skills (never seen by the analyzer) for deep content
7146
+ * analysis. When false, the scan still looks skills up by content hash and
7147
+ * still flags known-risky ones — but no skill content ever leaves the
7148
+ * machine and the upload worker never runs (lookup-only mode).
7149
+ */
7150
+ upload_enabled: external_exports.boolean().default(true)
7151
+ });
7104
7152
  var PiCheckConfigSchema = external_exports.object({
7105
7153
  enabled: external_exports.boolean().default(false),
7106
7154
  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)
7155
+ model_path: external_exports.string().optional()
7110
7156
  });
7111
7157
  var ExceptionDecisionSchema = external_exports.enum(["allow", "deny"]);
7112
7158
  var ExceptionMatchSchema = external_exports.enum(["executable", "domain", "path", "plugin", "regex"]);
@@ -7128,6 +7174,7 @@ var ConfigSchema = external_exports.object({
7128
7174
  file_check: FileCheckConfigSchema.default({}),
7129
7175
  package_check: PackageCheckConfigSchema.default({}),
7130
7176
  amsi_check: AmsiCheckConfigSchema.default({}),
7177
+ skill_check: SkillCheckConfigSchema.default({}),
7131
7178
  pi_check: PiCheckConfigSchema.default({}),
7132
7179
  heuristics_enabled: external_exports.boolean().default(true),
7133
7180
  cache: CacheConfigSchema.default({}),
@@ -7136,6 +7183,8 @@ var ConfigSchema = external_exports.object({
7136
7183
  operational_logging: OperationalLoggingConfigSchema.default({}),
7137
7184
  sensitivity: SensitivitySchema.default("balanced"),
7138
7185
  disabled_threats: external_exports.array(external_exports.string()).default([]),
7186
+ announce_clean_scans: external_exports.boolean().default(true),
7187
+ manage_status_line: external_exports.boolean().default(true),
7139
7188
  brand_key: external_exports.string().min(1).max(32).regex(/^[a-z0-9_-]+$/u).optional(),
7140
7189
  community_iq: external_exports.boolean().default(true)
7141
7190
  });
@@ -7150,6 +7199,8 @@ var HookTypeSchema = external_exports.enum([
7150
7199
 
7151
7200
  // ../core/src/config.ts
7152
7201
  var SAGE_DIR = "~/.sage";
7202
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
7203
+ var CLOCK_SKEW_TOLERANCE_MS = 5 * 60 * 1e3;
7153
7204
  function resolvedSageDir() {
7154
7205
  return resolvePath(SAGE_DIR);
7155
7206
  }
@@ -7216,7 +7267,7 @@ function sanitizeBrandKey(data, logger) {
7216
7267
  if (typeof brandKey === "string" && brandKey.length >= 1 && brandKey.length <= 32 && BRAND_KEY_RE.test(brandKey)) {
7217
7268
  return data;
7218
7269
  }
7219
- logger.warn(`Invalid brand_key in config \u2014 ignoring`, { brand_key: brandKey });
7270
+ logger.warn(`Invalid brand_key in config - ignoring`, { brand_key: brandKey });
7220
7271
  const { brand_key: _2, ...rest } = data;
7221
7272
  return rest;
7222
7273
  }
@@ -7284,7 +7335,7 @@ async function loadConfig(configPath, logger = nullLogger) {
7284
7335
 
7285
7336
  // ../core/src/installation-id.ts
7286
7337
  var import_node_crypto = require("node:crypto");
7287
- var import_promises = require("node:fs/promises");
7338
+ var import_promises2 = require("node:fs/promises");
7288
7339
  var import_node_path2 = require("node:path");
7289
7340
  async function getInstallationId(sageDirPath) {
7290
7341
  const sageDir = sageDirPath ?? resolvePath("~/.sage");
@@ -7299,8 +7350,8 @@ async function getInstallationId(sageDirPath) {
7299
7350
  }
7300
7351
  try {
7301
7352
  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" });
7353
+ await (0, import_promises2.mkdir)(sageDir, { recursive: true, mode: 448 });
7354
+ await (0, import_promises2.writeFile)(idPath, id, { encoding: "utf-8", mode: 384, flag: fileExists ? "w" : "wx" });
7304
7355
  return id;
7305
7356
  } catch (err) {
7306
7357
  if (err.code === "EEXIST") {
@@ -7318,17 +7369,18 @@ async function getInstallationId(sageDirPath) {
7318
7369
  // ../core/src/clients/model-downloader.ts
7319
7370
  var import_node_crypto3 = require("node:crypto");
7320
7371
  var import_node_fs8 = require("node:fs");
7321
- var import_promises3 = require("node:fs/promises");
7372
+ var import_promises4 = require("node:fs/promises");
7322
7373
  var import_node_path13 = require("node:path");
7323
7374
  var import_node_stream2 = require("node:stream");
7324
- var import_promises4 = require("node:stream/promises");
7375
+ var import_promises5 = require("node:stream/promises");
7325
7376
 
7326
7377
  // ../core/src/model-storage.ts
7327
7378
  var import_node_fs = require("node:fs");
7328
7379
  var import_node_path3 = require("node:path");
7329
- var MODEL_SCHEMA_VERSION = "v1";
7380
+ var MODEL_SCHEMA_VERSION = "v2";
7330
7381
  var REQUIRED_MODELS_BY_SCHEMA = {
7331
- v1: ["pi-model"]
7382
+ v1: ["pi-model"],
7383
+ v2: ["pi-model"]
7332
7384
  };
7333
7385
  var REQUIRED_MODEL_FILES = {
7334
7386
  "pi-model": [
@@ -7377,8 +7429,8 @@ async function downloadModel(opts) {
7377
7429
  const stagingDir = getDownloadStagingDir(sageDir);
7378
7430
  const lockPath = `${modelDir}.lock`;
7379
7431
  try {
7380
- await (0, import_promises3.mkdir)(stagingDir, { recursive: true });
7381
- await (0, import_promises3.mkdir)((0, import_node_path13.resolve)(modelDir, ".."), { recursive: true });
7432
+ await (0, import_promises4.mkdir)(stagingDir, { recursive: true });
7433
+ await (0, import_promises4.mkdir)((0, import_node_path13.resolve)(modelDir, ".."), { recursive: true });
7382
7434
  } catch (err) {
7383
7435
  logger.warn(`failed to prepare model storage for ${modelName}: ${err}`);
7384
7436
  return false;
@@ -7404,7 +7456,7 @@ async function downloadModel(opts) {
7404
7456
  });
7405
7457
  return false;
7406
7458
  }
7407
- await (0, import_promises3.mkdir)(extractDir, { recursive: true });
7459
+ await (0, import_promises4.mkdir)(extractDir, { recursive: true });
7408
7460
  await extractTarGz(archivePath, extractDir);
7409
7461
  const extractDirResolved = extractDir;
7410
7462
  const missing = requiredModelFiles(modelName).filter(
@@ -7417,26 +7469,26 @@ async function downloadModel(opts) {
7417
7469
  const aside = `${modelDir}.${suffix}.old`;
7418
7470
  let asideUsed = false;
7419
7471
  if (await pathExists(modelDir)) {
7420
- await (0, import_promises3.rename)(modelDir, aside);
7472
+ await (0, import_promises4.rename)(modelDir, aside);
7421
7473
  asideUsed = true;
7422
7474
  }
7423
7475
  try {
7424
- await (0, import_promises3.rename)(extractDir, modelDir);
7476
+ await (0, import_promises4.rename)(extractDir, modelDir);
7425
7477
  extractDir = null;
7426
7478
  } catch (err) {
7427
7479
  if (asideUsed) {
7428
7480
  try {
7429
- await (0, import_promises3.rename)(aside, modelDir);
7481
+ await (0, import_promises4.rename)(aside, modelDir);
7430
7482
  } catch {
7431
7483
  }
7432
7484
  }
7433
7485
  throw err;
7434
7486
  }
7435
7487
  if (asideUsed) {
7436
- await (0, import_promises3.rm)(aside, { recursive: true, force: true }).catch(() => {
7488
+ await (0, import_promises4.rm)(aside, { recursive: true, force: true }).catch(() => {
7437
7489
  });
7438
7490
  }
7439
- await (0, import_promises3.unlink)(archivePath).catch(() => {
7491
+ await (0, import_promises4.unlink)(archivePath).catch(() => {
7440
7492
  });
7441
7493
  archivePath = null;
7442
7494
  logger.info(`model ${modelName} installed (schema ${schema})`);
@@ -7448,14 +7500,14 @@ async function downloadModel(opts) {
7448
7500
  return false;
7449
7501
  } finally {
7450
7502
  if (extractDir) {
7451
- await (0, import_promises3.rm)(extractDir, { recursive: true, force: true }).catch(() => {
7503
+ await (0, import_promises4.rm)(extractDir, { recursive: true, force: true }).catch(() => {
7452
7504
  });
7453
7505
  }
7454
7506
  if (archivePath) {
7455
- await (0, import_promises3.unlink)(archivePath).catch(() => {
7507
+ await (0, import_promises4.unlink)(archivePath).catch(() => {
7456
7508
  });
7457
7509
  }
7458
- await (0, import_promises3.unlink)(lockPath).catch(() => {
7510
+ await (0, import_promises4.unlink)(lockPath).catch(() => {
7459
7511
  });
7460
7512
  }
7461
7513
  }
@@ -7468,11 +7520,11 @@ async function streamFetchToFile(url, destPath, fetchImpl = fetch) {
7468
7520
  throw new Error(`empty response body from ${url}`);
7469
7521
  }
7470
7522
  const nodeStream = import_node_stream2.Readable.fromWeb(response.body);
7471
- await (0, import_promises4.pipeline)(nodeStream, (0, import_node_fs8.createWriteStream)(destPath));
7523
+ await (0, import_promises5.pipeline)(nodeStream, (0, import_node_fs8.createWriteStream)(destPath));
7472
7524
  }
7473
7525
  async function sha256OfFile(path) {
7474
7526
  const hash = (0, import_node_crypto3.createHash)("sha256");
7475
- const buf = await (0, import_promises3.readFile)(path);
7527
+ const buf = await (0, import_promises4.readFile)(path);
7476
7528
  hash.update(buf);
7477
7529
  return hash.digest("hex");
7478
7530
  }
@@ -7486,7 +7538,7 @@ async function extractTarGz(archivePath, destDir) {
7486
7538
  }
7487
7539
  async function pathExists(path) {
7488
7540
  try {
7489
- await (0, import_promises3.stat)(path);
7541
+ await (0, import_promises4.stat)(path);
7490
7542
  return true;
7491
7543
  } catch {
7492
7544
  return false;
@@ -7503,7 +7555,7 @@ function fileExistsSync(path) {
7503
7555
  async function tryAcquireLock(lockPath, logger) {
7504
7556
  try {
7505
7557
  const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
7506
- await (0, import_promises3.writeFile)(lockPath, payload, { flag: "wx" });
7558
+ await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
7507
7559
  return true;
7508
7560
  } catch (err) {
7509
7561
  const code = err.code;
@@ -7513,15 +7565,15 @@ async function tryAcquireLock(lockPath, logger) {
7513
7565
  }
7514
7566
  }
7515
7567
  try {
7516
- const raw = await (0, import_promises3.readFile)(lockPath, "utf-8");
7568
+ const raw = await (0, import_promises4.readFile)(lockPath, "utf-8");
7517
7569
  const parsed = JSON.parse(raw);
7518
7570
  const age = Date.now() - (parsed.ts ?? 0);
7519
7571
  if (age > STALE_LOCK_MS) {
7520
- await (0, import_promises3.unlink)(lockPath).catch(() => {
7572
+ await (0, import_promises4.unlink)(lockPath).catch(() => {
7521
7573
  });
7522
7574
  try {
7523
7575
  const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
7524
- await (0, import_promises3.writeFile)(lockPath, payload, { flag: "wx" });
7576
+ await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
7525
7577
  return true;
7526
7578
  } catch {
7527
7579
  return false;
@@ -7533,7 +7585,7 @@ async function tryAcquireLock(lockPath, logger) {
7533
7585
  }
7534
7586
 
7535
7587
  // ../core/src/sage-proxy.ts
7536
- function mapSageProxyOs(platform) {
7588
+ function mapSageHostOs(platform) {
7537
7589
  switch (platform) {
7538
7590
  case "win32":
7539
7591
  return "WINDOWS";
@@ -7545,21 +7597,37 @@ function mapSageProxyOs(platform) {
7545
7597
  return platform;
7546
7598
  }
7547
7599
  }
7548
- function mapSageProxyArchitecture(arch) {
7600
+ function mapSageHostArchitecture(arch) {
7549
7601
  return arch.toUpperCase();
7550
7602
  }
7603
+ function buildSageUserConfig(config) {
7604
+ return {
7605
+ sensitivity: config.sensitivity,
7606
+ url_check_enabled: config.url_check.enabled,
7607
+ file_check_enabled: config.file_check.enabled,
7608
+ package_check_enabled: config.package_check.enabled,
7609
+ heuristics_enabled: config.heuristics_enabled,
7610
+ pi_check_enabled: config.pi_check.enabled,
7611
+ community_iq_enabled: config.community_iq,
7612
+ // Optional-chained + defaulted: real callers pass a loadConfig result (skill_check
7613
+ // always present via the schema default), but this feeds fail-open telemetry, so a
7614
+ // partial config must never crash the envelope build.
7615
+ skill_check_upload_enabled: config.skill_check?.upload_enabled ?? true
7616
+ };
7617
+ }
7551
7618
  function buildSageProxyEnvelope(args) {
7552
7619
  return {
7553
7620
  identity: { uuid: args.iid },
7554
7621
  product: { version_app: args.versionApp },
7555
7622
  platform: {
7556
- os: args.platformOs ?? mapSageProxyOs(process.platform),
7557
- architecture: args.platformArchitecture ?? mapSageProxyArchitecture(process.arch)
7623
+ os: args.platformOs ?? mapSageHostOs(process.platform),
7624
+ architecture: args.platformArchitecture ?? mapSageHostArchitecture(process.arch)
7558
7625
  },
7559
7626
  agent: {
7560
7627
  agent_runtime: args.agentRuntime,
7561
7628
  agent_runtime_version: args.agentRuntimeVersion
7562
- }
7629
+ },
7630
+ ...args.config ? { config: buildSageUserConfig(args.config) } : {}
7563
7631
  };
7564
7632
  }
7565
7633
 
@@ -7568,7 +7636,7 @@ var import_node_path14 = require("node:path");
7568
7636
  var import_node_url = require("node:url");
7569
7637
  var import_meta = {};
7570
7638
  function resolveVersion() {
7571
- if (true) return "0.11.0";
7639
+ if (true) return "0.13.0";
7572
7640
  try {
7573
7641
  const pkgPath = (0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
7574
7642
  const pkg = JSON.parse(getFileContentSync(pkgPath));
@@ -7607,14 +7675,23 @@ async function fetchModelManifest(args) {
7607
7675
  logger.debug("Skipping model manifest fetch: missing installation id");
7608
7676
  return null;
7609
7677
  }
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 } };
7678
+ let config = args.config;
7679
+ if (!config) {
7680
+ try {
7681
+ config = await loadConfig(args.configPath, logger);
7682
+ } catch (err) {
7683
+ logger.debug(`Model manifest config load failed: ${err}`);
7684
+ }
7685
+ }
7617
7686
  try {
7687
+ const envelope = buildSageProxyEnvelope({
7688
+ iid: args.iid,
7689
+ versionApp: args.versionApp ?? VERSION,
7690
+ agentRuntime: args.agentRuntime,
7691
+ agentRuntimeVersion: args.agentRuntimeVersion ?? "unknown",
7692
+ config
7693
+ });
7694
+ const body = { ...envelope, models: { schema: args.schema } };
7618
7695
  const response = await fetch(resolveEndpoint("/v2/model-manifest"), {
7619
7696
  method: "POST",
7620
7697
  signal: AbortSignal.timeout(timeoutMs),
@@ -7691,6 +7768,8 @@ async function ensureModelsAvailable(args) {
7691
7768
  schema,
7692
7769
  agentRuntime: args.agentRuntime ?? "unknown",
7693
7770
  agentRuntimeVersion: args.agentRuntimeVersion,
7771
+ config: args.config,
7772
+ configPath: args.configPath,
7694
7773
  versionApp: args.versionApp,
7695
7774
  logger
7696
7775
  });
@@ -7736,78 +7815,42 @@ async function ensureModelsAvailable(args) {
7736
7815
  }
7737
7816
 
7738
7817
  // ../core/src/jsonl-log-writer.ts
7739
- var import_promises5 = require("node:fs/promises");
7818
+ var import_promises6 = require("node:fs/promises");
7740
7819
  var import_node_path15 = require("node:path");
7741
- var import_promises6 = require("node:timers/promises");
7742
7820
  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
7821
  async function shouldRotate(filePath, maxBytes, maxFiles) {
7747
7822
  if (maxBytes <= 0 || maxFiles <= 0) return false;
7748
7823
  try {
7749
- const s3 = await (0, import_promises5.stat)(filePath);
7824
+ const s3 = await (0, import_promises6.stat)(filePath);
7750
7825
  return s3.size >= maxBytes;
7751
7826
  } catch {
7752
7827
  return false;
7753
7828
  }
7754
7829
  }
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
7830
  async function rotateIfNeeded(filePath, maxBytes, maxFiles) {
7784
7831
  if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
7785
- const releaseLock = await acquireRotateLock(filePath);
7786
- if (!releaseLock) return;
7787
- try {
7832
+ await withFileLock(filePath, async () => {
7788
7833
  if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
7789
7834
  try {
7790
- await (0, import_promises5.unlink)(`${filePath}.${maxFiles}`);
7835
+ await (0, import_promises6.unlink)(`${filePath}.${maxFiles}`);
7791
7836
  } catch {
7792
7837
  }
7793
7838
  for (let i = maxFiles - 1; i >= 1; i--) {
7794
7839
  try {
7795
- await (0, import_promises5.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
7840
+ await (0, import_promises6.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
7796
7841
  } catch {
7797
7842
  }
7798
7843
  }
7799
7844
  try {
7800
- await (0, import_promises5.rename)(filePath, `${filePath}.1`);
7845
+ await (0, import_promises6.rename)(filePath, `${filePath}.1`);
7801
7846
  } catch {
7802
7847
  }
7803
- } finally {
7804
- await releaseLock();
7805
- }
7848
+ });
7806
7849
  }
7807
7850
  async function appendJsonlEntryNow(path, config, entry) {
7808
- await (0, import_promises5.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
7851
+ await (0, import_promises6.mkdir)((0, import_node_path15.dirname)(path), { recursive: true });
7809
7852
  await rotateIfNeeded(path, config.max_bytes, config.max_files);
7810
- await (0, import_promises5.appendFile)(path, `${JSON.stringify(entry)}
7853
+ await (0, import_promises6.appendFile)(path, `${JSON.stringify(entry)}
7811
7854
  `);
7812
7855
  }
7813
7856
  async function appendJsonlEntry(config, entry) {
@@ -7937,11 +7980,12 @@ function createOperationalLogger(config, runtime) {
7937
7980
  async function readWorkerArgs() {
7938
7981
  const env = process.env;
7939
7982
  const sageDir = env.SAGE_DIR ? resolvePath(env.SAGE_DIR) : resolvePath("~/.sage");
7983
+ const configPath = env.SAGE_CONFIG_PATH ? resolvePath(env.SAGE_CONFIG_PATH) : void 0;
7940
7984
  const agentRuntime = env.SAGE_AGENT_RUNTIME ?? "unknown";
7941
7985
  const agentRuntimeVersion = env.SAGE_AGENT_RUNTIME_VERSION || void 0;
7942
7986
  const versionApp = env.SAGE_VERSION_APP || void 0;
7943
7987
  const schema = env.SAGE_MODEL_SCHEMA || MODEL_SCHEMA_VERSION;
7944
- return { sageDir, agentRuntime, agentRuntimeVersion, versionApp, schema };
7988
+ return { sageDir, configPath, agentRuntime, agentRuntimeVersion, versionApp, schema };
7945
7989
  }
7946
7990
  async function createWorkerLogger(agentRuntime) {
7947
7991
  try {
@@ -7981,6 +8025,7 @@ async function workerMain() {
7981
8025
  iid,
7982
8026
  agentRuntime: args.agentRuntime,
7983
8027
  agentRuntimeVersion: args.agentRuntimeVersion,
8028
+ configPath: args.configPath,
7984
8029
  versionApp: args.versionApp,
7985
8030
  schema: args.schema,
7986
8031
  logger