@gendigital/sage-openclaw 0.10.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.
Files changed (37) hide show
  1. package/README.md +5 -5
  2. package/dist/index.cjs +1881 -977
  3. package/dist/index.cjs.map +4 -4
  4. package/dist/mcp-server.cjs +31882 -0
  5. package/dist/mcp-server.cjs.map +7 -0
  6. package/dist/model-download-worker.cjs +140 -111
  7. package/dist/model-download-worker.cjs.map +3 -3
  8. package/dist/skill-upload-worker.cjs +5632 -0
  9. package/dist/skill-upload-worker.cjs.map +7 -0
  10. package/openclaw.plugin.json +6 -1
  11. package/package.json +9 -3
  12. package/resources/skills/security-awareness/SKILL.md +5 -1
  13. package/resources/threats/agent-layer.yaml +186 -0
  14. package/resources/threats/commands.yaml +24 -53
  15. package/resources/threats/credentials.yaml +12 -20
  16. package/resources/threats/dummy.yaml +44 -50
  17. package/resources/threats/files.yaml +34 -53
  18. package/resources/threats/mac-commands.yaml +20 -47
  19. package/resources/threats/mac-credentials.yaml +4 -12
  20. package/resources/threats/mac-files.yaml +5 -15
  21. package/resources/threats/mac-obfuscation.yaml +6 -13
  22. package/resources/threats/mac-persistence.yaml +11 -20
  23. package/resources/threats/mac-supply-chain.yaml +5 -8
  24. package/resources/threats/mitre.yaml +105 -167
  25. package/resources/threats/obfuscation.yaml +7 -15
  26. package/resources/threats/persistence.yaml +10 -17
  27. package/resources/threats/prompt-injection.yaml +92 -43
  28. package/resources/threats/self-defense.yaml +8 -12
  29. package/resources/threats/supply_chain.yaml +1 -2
  30. package/resources/threats/urls.yaml +9 -14
  31. package/resources/threats/win-commands.yaml +46 -106
  32. package/resources/threats/win-credentials.yaml +14 -24
  33. package/resources/threats/win-files.yaml +10 -20
  34. package/resources/threats/win-obfuscation.yaml +21 -37
  35. package/resources/threats/win-persistence.yaml +8 -16
  36. package/resources/threats/win-supply-chain.yaml +2 -3
  37. /package/resources/{allowlists → trusted-domains}/trusted-installer-domains.yaml +0 -0
@@ -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 = {};
@@ -7043,23 +7081,21 @@ var ArtifactSchema = external_exports.object({
7043
7081
  value: external_exports.string(),
7044
7082
  context: external_exports.string().optional()
7045
7083
  });
7046
- var SeveritySchema = external_exports.enum(["critical", "high", "medium", "low"]);
7047
- var ActionSchema = external_exports.enum(["block", "require_approval", "log"]);
7084
+ var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
7048
7085
  var ThreatSchema = external_exports.object({
7049
7086
  id: external_exports.string(),
7050
7087
  version: external_exports.number().int().optional(),
7051
7088
  category: external_exports.string(),
7052
- severity: SeveritySchema,
7089
+ severity: VerdictSeveritySchema,
7053
7090
  confidence: external_exports.number(),
7054
- action: ActionSchema,
7055
7091
  pattern: external_exports.string(),
7056
7092
  match_on: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]),
7057
7093
  title: external_exports.string(),
7058
7094
  expires_at: external_exports.string().nullable().optional(),
7059
- revoked: external_exports.boolean().optional().default(false)
7095
+ revoked: external_exports.boolean().optional().default(false),
7096
+ flags: external_exports.array(external_exports.string()).optional().default([])
7060
7097
  });
7061
7098
  var DecisionSchema = external_exports.enum(["allow", "deny", "ask"]);
7062
- var VerdictSeveritySchema = external_exports.enum(["info", "warning", "critical"]);
7063
7099
  var SensitivitySchema = external_exports.enum(["paranoid", "balanced", "relaxed"]);
7064
7100
  var UrlCheckConfigSchema = external_exports.object({
7065
7101
  endpoint: external_exports.string().optional(),
@@ -7072,9 +7108,6 @@ var CacheConfigSchema = external_exports.object({
7072
7108
  ttl_clean_seconds: external_exports.number().default(86400),
7073
7109
  path: external_exports.string().default("~/.sage/cache.json")
7074
7110
  });
7075
- var AllowlistConfigSchema = external_exports.object({
7076
- path: external_exports.string().default("~/.sage/allowlist.json")
7077
- });
7078
7111
  var LoggingConfigSchema = external_exports.object({
7079
7112
  enabled: external_exports.boolean().default(true),
7080
7113
  log_clean: external_exports.boolean().default(false),
@@ -7104,14 +7137,21 @@ var PackageCheckConfigSchema = external_exports.object({
7104
7137
  var AmsiCheckConfigSchema = external_exports.object({
7105
7138
  enabled: external_exports.boolean().default(true)
7106
7139
  });
7107
- var DEFAULT_PI_HIGH_RISK_THRESHOLD = 0.99;
7108
- 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
+ });
7109
7151
  var PiCheckConfigSchema = external_exports.object({
7110
7152
  enabled: external_exports.boolean().default(false),
7111
7153
  max_content_length: external_exports.number().default(16384),
7112
- model_path: external_exports.string().optional(),
7113
- high_risk_threshold: external_exports.number().default(DEFAULT_PI_HIGH_RISK_THRESHOLD),
7114
- medium_risk_threshold: external_exports.number().default(DEFAULT_PI_MEDIUM_RISK_THRESHOLD)
7154
+ model_path: external_exports.string().optional()
7115
7155
  });
7116
7156
  var ExceptionDecisionSchema = external_exports.enum(["allow", "deny"]);
7117
7157
  var ExceptionMatchSchema = external_exports.enum(["executable", "domain", "path", "plugin", "regex"]);
@@ -7133,15 +7173,16 @@ var ConfigSchema = external_exports.object({
7133
7173
  file_check: FileCheckConfigSchema.default({}),
7134
7174
  package_check: PackageCheckConfigSchema.default({}),
7135
7175
  amsi_check: AmsiCheckConfigSchema.default({}),
7176
+ skill_check: SkillCheckConfigSchema.default({}),
7136
7177
  pi_check: PiCheckConfigSchema.default({}),
7137
7178
  heuristics_enabled: external_exports.boolean().default(true),
7138
7179
  cache: CacheConfigSchema.default({}),
7139
- allowlist: AllowlistConfigSchema.default({}),
7140
7180
  exceptions: ExceptionsConfigSchema.default({}),
7141
7181
  logging: LoggingConfigSchema.default({}),
7142
7182
  operational_logging: OperationalLoggingConfigSchema.default({}),
7143
7183
  sensitivity: SensitivitySchema.default("balanced"),
7144
7184
  disabled_threats: external_exports.array(external_exports.string()).default([]),
7185
+ announce_clean_scans: external_exports.boolean().default(true),
7145
7186
  brand_key: external_exports.string().min(1).max(32).regex(/^[a-z0-9_-]+$/u).optional(),
7146
7187
  community_iq: external_exports.boolean().default(true)
7147
7188
  });
@@ -7156,6 +7197,8 @@ var HookTypeSchema = external_exports.enum([
7156
7197
 
7157
7198
  // ../core/src/config.ts
7158
7199
  var SAGE_DIR = "~/.sage";
7200
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
7201
+ var CLOCK_SKEW_TOLERANCE_MS = 5 * 60 * 1e3;
7159
7202
  function resolvedSageDir() {
7160
7203
  return resolvePath(SAGE_DIR);
7161
7204
  }
@@ -7165,9 +7208,6 @@ function defaultConfigPath() {
7165
7208
  function defaultCachePath() {
7166
7209
  return (0, import_node_path.join)(resolvedSageDir(), "cache.json");
7167
7210
  }
7168
- function defaultAllowlistPath() {
7169
- return (0, import_node_path.join)(resolvedSageDir(), "allowlist.json");
7170
- }
7171
7211
  function defaultExceptionsPath() {
7172
7212
  return (0, import_node_path.join)(resolvedSageDir(), "exceptions.json");
7173
7213
  }
@@ -7225,13 +7265,12 @@ function sanitizeBrandKey(data, logger) {
7225
7265
  if (typeof brandKey === "string" && brandKey.length >= 1 && brandKey.length <= 32 && BRAND_KEY_RE.test(brandKey)) {
7226
7266
  return data;
7227
7267
  }
7228
- logger.warn(`Invalid brand_key in config \u2014 ignoring`, { brand_key: brandKey });
7268
+ logger.warn(`Invalid brand_key in config - ignoring`, { brand_key: brandKey });
7229
7269
  const { brand_key: _2, ...rest } = data;
7230
7270
  return rest;
7231
7271
  }
7232
7272
  function sanitizeConfigPaths(config, logger) {
7233
7273
  const cachePath = defaultCachePath();
7234
- const allowlistPath = defaultAllowlistPath();
7235
7274
  const exceptionsPath = defaultExceptionsPath();
7236
7275
  const auditPath = defaultAuditPath();
7237
7276
  const operationalLogPath = defaultOperationalLogPath();
@@ -7241,10 +7280,6 @@ function sanitizeConfigPaths(config, logger) {
7241
7280
  ...config.cache,
7242
7281
  path: normalizeStateFilePath(config.cache.path, cachePath, "cache", logger)
7243
7282
  },
7244
- allowlist: {
7245
- ...config.allowlist,
7246
- path: normalizeStateFilePath(config.allowlist.path, allowlistPath, "allowlist", logger)
7247
- },
7248
7283
  exceptions: {
7249
7284
  ...config.exceptions,
7250
7285
  path: normalizeStateFilePath(config.exceptions.path, exceptionsPath, "exceptions", logger)
@@ -7298,7 +7333,7 @@ async function loadConfig(configPath, logger = nullLogger) {
7298
7333
 
7299
7334
  // ../core/src/installation-id.ts
7300
7335
  var import_node_crypto = require("node:crypto");
7301
- var import_promises = require("node:fs/promises");
7336
+ var import_promises2 = require("node:fs/promises");
7302
7337
  var import_node_path2 = require("node:path");
7303
7338
  async function getInstallationId(sageDirPath) {
7304
7339
  const sageDir = sageDirPath ?? resolvePath("~/.sage");
@@ -7313,8 +7348,8 @@ async function getInstallationId(sageDirPath) {
7313
7348
  }
7314
7349
  try {
7315
7350
  const id = (0, import_node_crypto.randomUUID)();
7316
- await (0, import_promises.mkdir)(sageDir, { recursive: true, mode: 448 });
7317
- 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" });
7318
7353
  return id;
7319
7354
  } catch (err) {
7320
7355
  if (err.code === "EEXIST") {
@@ -7332,17 +7367,18 @@ async function getInstallationId(sageDirPath) {
7332
7367
  // ../core/src/clients/model-downloader.ts
7333
7368
  var import_node_crypto3 = require("node:crypto");
7334
7369
  var import_node_fs8 = require("node:fs");
7335
- var import_promises3 = require("node:fs/promises");
7370
+ var import_promises4 = require("node:fs/promises");
7336
7371
  var import_node_path13 = require("node:path");
7337
7372
  var import_node_stream2 = require("node:stream");
7338
- var import_promises4 = require("node:stream/promises");
7373
+ var import_promises5 = require("node:stream/promises");
7339
7374
 
7340
7375
  // ../core/src/model-storage.ts
7341
7376
  var import_node_fs = require("node:fs");
7342
7377
  var import_node_path3 = require("node:path");
7343
- var MODEL_SCHEMA_VERSION = "v1";
7378
+ var MODEL_SCHEMA_VERSION = "v2";
7344
7379
  var REQUIRED_MODELS_BY_SCHEMA = {
7345
- v1: ["pi-model"]
7380
+ v1: ["pi-model"],
7381
+ v2: ["pi-model"]
7346
7382
  };
7347
7383
  var REQUIRED_MODEL_FILES = {
7348
7384
  "pi-model": [
@@ -7391,8 +7427,8 @@ async function downloadModel(opts) {
7391
7427
  const stagingDir = getDownloadStagingDir(sageDir);
7392
7428
  const lockPath = `${modelDir}.lock`;
7393
7429
  try {
7394
- await (0, import_promises3.mkdir)(stagingDir, { recursive: true });
7395
- 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 });
7396
7432
  } catch (err) {
7397
7433
  logger.warn(`failed to prepare model storage for ${modelName}: ${err}`);
7398
7434
  return false;
@@ -7418,7 +7454,7 @@ async function downloadModel(opts) {
7418
7454
  });
7419
7455
  return false;
7420
7456
  }
7421
- await (0, import_promises3.mkdir)(extractDir, { recursive: true });
7457
+ await (0, import_promises4.mkdir)(extractDir, { recursive: true });
7422
7458
  await extractTarGz(archivePath, extractDir);
7423
7459
  const extractDirResolved = extractDir;
7424
7460
  const missing = requiredModelFiles(modelName).filter(
@@ -7431,26 +7467,26 @@ async function downloadModel(opts) {
7431
7467
  const aside = `${modelDir}.${suffix}.old`;
7432
7468
  let asideUsed = false;
7433
7469
  if (await pathExists(modelDir)) {
7434
- await (0, import_promises3.rename)(modelDir, aside);
7470
+ await (0, import_promises4.rename)(modelDir, aside);
7435
7471
  asideUsed = true;
7436
7472
  }
7437
7473
  try {
7438
- await (0, import_promises3.rename)(extractDir, modelDir);
7474
+ await (0, import_promises4.rename)(extractDir, modelDir);
7439
7475
  extractDir = null;
7440
7476
  } catch (err) {
7441
7477
  if (asideUsed) {
7442
7478
  try {
7443
- await (0, import_promises3.rename)(aside, modelDir);
7479
+ await (0, import_promises4.rename)(aside, modelDir);
7444
7480
  } catch {
7445
7481
  }
7446
7482
  }
7447
7483
  throw err;
7448
7484
  }
7449
7485
  if (asideUsed) {
7450
- await (0, import_promises3.rm)(aside, { recursive: true, force: true }).catch(() => {
7486
+ await (0, import_promises4.rm)(aside, { recursive: true, force: true }).catch(() => {
7451
7487
  });
7452
7488
  }
7453
- await (0, import_promises3.unlink)(archivePath).catch(() => {
7489
+ await (0, import_promises4.unlink)(archivePath).catch(() => {
7454
7490
  });
7455
7491
  archivePath = null;
7456
7492
  logger.info(`model ${modelName} installed (schema ${schema})`);
@@ -7462,14 +7498,14 @@ async function downloadModel(opts) {
7462
7498
  return false;
7463
7499
  } finally {
7464
7500
  if (extractDir) {
7465
- await (0, import_promises3.rm)(extractDir, { recursive: true, force: true }).catch(() => {
7501
+ await (0, import_promises4.rm)(extractDir, { recursive: true, force: true }).catch(() => {
7466
7502
  });
7467
7503
  }
7468
7504
  if (archivePath) {
7469
- await (0, import_promises3.unlink)(archivePath).catch(() => {
7505
+ await (0, import_promises4.unlink)(archivePath).catch(() => {
7470
7506
  });
7471
7507
  }
7472
- await (0, import_promises3.unlink)(lockPath).catch(() => {
7508
+ await (0, import_promises4.unlink)(lockPath).catch(() => {
7473
7509
  });
7474
7510
  }
7475
7511
  }
@@ -7482,11 +7518,11 @@ async function streamFetchToFile(url, destPath, fetchImpl = fetch) {
7482
7518
  throw new Error(`empty response body from ${url}`);
7483
7519
  }
7484
7520
  const nodeStream = import_node_stream2.Readable.fromWeb(response.body);
7485
- 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));
7486
7522
  }
7487
7523
  async function sha256OfFile(path) {
7488
7524
  const hash = (0, import_node_crypto3.createHash)("sha256");
7489
- const buf = await (0, import_promises3.readFile)(path);
7525
+ const buf = await (0, import_promises4.readFile)(path);
7490
7526
  hash.update(buf);
7491
7527
  return hash.digest("hex");
7492
7528
  }
@@ -7500,7 +7536,7 @@ async function extractTarGz(archivePath, destDir) {
7500
7536
  }
7501
7537
  async function pathExists(path) {
7502
7538
  try {
7503
- await (0, import_promises3.stat)(path);
7539
+ await (0, import_promises4.stat)(path);
7504
7540
  return true;
7505
7541
  } catch {
7506
7542
  return false;
@@ -7517,7 +7553,7 @@ function fileExistsSync(path) {
7517
7553
  async function tryAcquireLock(lockPath, logger) {
7518
7554
  try {
7519
7555
  const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
7520
- await (0, import_promises3.writeFile)(lockPath, payload, { flag: "wx" });
7556
+ await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
7521
7557
  return true;
7522
7558
  } catch (err) {
7523
7559
  const code = err.code;
@@ -7527,15 +7563,15 @@ async function tryAcquireLock(lockPath, logger) {
7527
7563
  }
7528
7564
  }
7529
7565
  try {
7530
- const raw = await (0, import_promises3.readFile)(lockPath, "utf-8");
7566
+ const raw = await (0, import_promises4.readFile)(lockPath, "utf-8");
7531
7567
  const parsed = JSON.parse(raw);
7532
7568
  const age = Date.now() - (parsed.ts ?? 0);
7533
7569
  if (age > STALE_LOCK_MS) {
7534
- await (0, import_promises3.unlink)(lockPath).catch(() => {
7570
+ await (0, import_promises4.unlink)(lockPath).catch(() => {
7535
7571
  });
7536
7572
  try {
7537
7573
  const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
7538
- await (0, import_promises3.writeFile)(lockPath, payload, { flag: "wx" });
7574
+ await (0, import_promises4.writeFile)(lockPath, payload, { flag: "wx" });
7539
7575
  return true;
7540
7576
  } catch {
7541
7577
  return false;
@@ -7547,7 +7583,7 @@ async function tryAcquireLock(lockPath, logger) {
7547
7583
  }
7548
7584
 
7549
7585
  // ../core/src/sage-proxy.ts
7550
- function mapSageProxyOs(platform) {
7586
+ function mapSageHostOs(platform) {
7551
7587
  switch (platform) {
7552
7588
  case "win32":
7553
7589
  return "WINDOWS";
@@ -7559,21 +7595,37 @@ function mapSageProxyOs(platform) {
7559
7595
  return platform;
7560
7596
  }
7561
7597
  }
7562
- function mapSageProxyArchitecture(arch) {
7598
+ function mapSageHostArchitecture(arch) {
7563
7599
  return arch.toUpperCase();
7564
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
+ }
7565
7616
  function buildSageProxyEnvelope(args) {
7566
7617
  return {
7567
7618
  identity: { uuid: args.iid },
7568
7619
  product: { version_app: args.versionApp },
7569
7620
  platform: {
7570
- os: args.platformOs ?? mapSageProxyOs(process.platform),
7571
- architecture: args.platformArchitecture ?? mapSageProxyArchitecture(process.arch)
7621
+ os: args.platformOs ?? mapSageHostOs(process.platform),
7622
+ architecture: args.platformArchitecture ?? mapSageHostArchitecture(process.arch)
7572
7623
  },
7573
7624
  agent: {
7574
7625
  agent_runtime: args.agentRuntime,
7575
7626
  agent_runtime_version: args.agentRuntimeVersion
7576
- }
7627
+ },
7628
+ ...args.config ? { config: buildSageUserConfig(args.config) } : {}
7577
7629
  };
7578
7630
  }
7579
7631
 
@@ -7582,7 +7634,7 @@ var import_node_path14 = require("node:path");
7582
7634
  var import_node_url = require("node:url");
7583
7635
  var import_meta = {};
7584
7636
  function resolveVersion() {
7585
- if (true) return "0.10.0";
7637
+ if (true) return "0.12.0";
7586
7638
  try {
7587
7639
  const pkgPath = (0, import_node_path14.join)((0, import_node_path14.dirname)((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
7588
7640
  const pkg = JSON.parse(getFileContentSync(pkgPath));
@@ -7621,14 +7673,23 @@ async function fetchModelManifest(args) {
7621
7673
  logger.debug("Skipping model manifest fetch: missing installation id");
7622
7674
  return null;
7623
7675
  }
7624
- const envelope = buildSageProxyEnvelope({
7625
- iid: args.iid,
7626
- versionApp: args.versionApp ?? VERSION,
7627
- agentRuntime: args.agentRuntime,
7628
- agentRuntimeVersion: args.agentRuntimeVersion ?? "unknown"
7629
- });
7630
- 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
+ }
7631
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 } };
7632
7693
  const response = await fetch(resolveEndpoint("/v2/model-manifest"), {
7633
7694
  method: "POST",
7634
7695
  signal: AbortSignal.timeout(timeoutMs),
@@ -7705,6 +7766,8 @@ async function ensureModelsAvailable(args) {
7705
7766
  schema,
7706
7767
  agentRuntime: args.agentRuntime ?? "unknown",
7707
7768
  agentRuntimeVersion: args.agentRuntimeVersion,
7769
+ config: args.config,
7770
+ configPath: args.configPath,
7708
7771
  versionApp: args.versionApp,
7709
7772
  logger
7710
7773
  });
@@ -7750,78 +7813,42 @@ async function ensureModelsAvailable(args) {
7750
7813
  }
7751
7814
 
7752
7815
  // ../core/src/jsonl-log-writer.ts
7753
- var import_promises5 = require("node:fs/promises");
7816
+ var import_promises6 = require("node:fs/promises");
7754
7817
  var import_node_path15 = require("node:path");
7755
- var import_promises6 = require("node:timers/promises");
7756
7818
  var writeQueues = /* @__PURE__ */ new Map();
7757
- var ROTATE_LOCK_TIMEOUT_MS = 250;
7758
- var ROTATE_LOCK_STALE_MS = 3e4;
7759
- var ROTATE_LOCK_POLL_MS = 50;
7760
7819
  async function shouldRotate(filePath, maxBytes, maxFiles) {
7761
7820
  if (maxBytes <= 0 || maxFiles <= 0) return false;
7762
7821
  try {
7763
- const s3 = await (0, import_promises5.stat)(filePath);
7822
+ const s3 = await (0, import_promises6.stat)(filePath);
7764
7823
  return s3.size >= maxBytes;
7765
7824
  } catch {
7766
7825
  return false;
7767
7826
  }
7768
7827
  }
7769
- async function removeStaleRotateLock(lockPath) {
7770
- try {
7771
- const s3 = await (0, import_promises5.stat)(lockPath);
7772
- if (Date.now() - s3.mtimeMs < ROTATE_LOCK_STALE_MS) return;
7773
- await (0, import_promises5.rmdir)(lockPath);
7774
- } catch {
7775
- }
7776
- }
7777
- async function acquireRotateLock(filePath) {
7778
- const lockPath = `${filePath}.lock`;
7779
- const deadline = Date.now() + ROTATE_LOCK_TIMEOUT_MS;
7780
- while (true) {
7781
- try {
7782
- await (0, import_promises5.mkdir)(lockPath);
7783
- return async () => {
7784
- try {
7785
- await (0, import_promises5.rmdir)(lockPath);
7786
- } catch {
7787
- }
7788
- };
7789
- } catch {
7790
- await removeStaleRotateLock(lockPath);
7791
- const remainingMs = deadline - Date.now();
7792
- if (remainingMs <= 0) return void 0;
7793
- await (0, import_promises6.setTimeout)(Math.min(ROTATE_LOCK_POLL_MS, remainingMs));
7794
- }
7795
- }
7796
- }
7797
7828
  async function rotateIfNeeded(filePath, maxBytes, maxFiles) {
7798
7829
  if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
7799
- const releaseLock = await acquireRotateLock(filePath);
7800
- if (!releaseLock) return;
7801
- try {
7830
+ await withFileLock(filePath, async () => {
7802
7831
  if (!await shouldRotate(filePath, maxBytes, maxFiles)) return;
7803
7832
  try {
7804
- await (0, import_promises5.unlink)(`${filePath}.${maxFiles}`);
7833
+ await (0, import_promises6.unlink)(`${filePath}.${maxFiles}`);
7805
7834
  } catch {
7806
7835
  }
7807
7836
  for (let i = maxFiles - 1; i >= 1; i--) {
7808
7837
  try {
7809
- await (0, import_promises5.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
7838
+ await (0, import_promises6.rename)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
7810
7839
  } catch {
7811
7840
  }
7812
7841
  }
7813
7842
  try {
7814
- await (0, import_promises5.rename)(filePath, `${filePath}.1`);
7843
+ await (0, import_promises6.rename)(filePath, `${filePath}.1`);
7815
7844
  } catch {
7816
7845
  }
7817
- } finally {
7818
- await releaseLock();
7819
- }
7846
+ });
7820
7847
  }
7821
7848
  async function appendJsonlEntryNow(path, config, entry) {
7822
- 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 });
7823
7850
  await rotateIfNeeded(path, config.max_bytes, config.max_files);
7824
- await (0, import_promises5.appendFile)(path, `${JSON.stringify(entry)}
7851
+ await (0, import_promises6.appendFile)(path, `${JSON.stringify(entry)}
7825
7852
  `);
7826
7853
  }
7827
7854
  async function appendJsonlEntry(config, entry) {
@@ -7951,11 +7978,12 @@ function createOperationalLogger(config, runtime) {
7951
7978
  async function readWorkerArgs() {
7952
7979
  const env = process.env;
7953
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;
7954
7982
  const agentRuntime = env.SAGE_AGENT_RUNTIME ?? "unknown";
7955
7983
  const agentRuntimeVersion = env.SAGE_AGENT_RUNTIME_VERSION || void 0;
7956
7984
  const versionApp = env.SAGE_VERSION_APP || void 0;
7957
7985
  const schema = env.SAGE_MODEL_SCHEMA || MODEL_SCHEMA_VERSION;
7958
- return { sageDir, agentRuntime, agentRuntimeVersion, versionApp, schema };
7986
+ return { sageDir, configPath, agentRuntime, agentRuntimeVersion, versionApp, schema };
7959
7987
  }
7960
7988
  async function createWorkerLogger(agentRuntime) {
7961
7989
  try {
@@ -7995,6 +8023,7 @@ async function workerMain() {
7995
8023
  iid,
7996
8024
  agentRuntime: args.agentRuntime,
7997
8025
  agentRuntimeVersion: args.agentRuntimeVersion,
8026
+ configPath: args.configPath,
7998
8027
  versionApp: args.versionApp,
7999
8028
  schema: args.schema,
8000
8029
  logger