@theokit/sdk 4.19.3 → 4.20.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 (43) hide show
  1. package/dist/cron.cjs +7 -3
  2. package/dist/cron.cjs.map +1 -1
  3. package/dist/cron.js +8 -4
  4. package/dist/cron.js.map +1 -1
  5. package/dist/eval.cjs +7 -3
  6. package/dist/eval.cjs.map +1 -1
  7. package/dist/eval.js +8 -4
  8. package/dist/eval.js.map +1 -1
  9. package/dist/filesystem/index.cjs +7 -3
  10. package/dist/filesystem/index.cjs.map +1 -1
  11. package/dist/filesystem/index.js +7 -3
  12. package/dist/filesystem/index.js.map +1 -1
  13. package/dist/index.cjs +7 -3
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +7 -3
  16. package/dist/index.js.map +1 -1
  17. package/dist/internal/security/index.cjs +7 -3
  18. package/dist/internal/security/index.cjs.map +1 -1
  19. package/dist/internal/security/index.js +8 -4
  20. package/dist/internal/security/index.js.map +1 -1
  21. package/dist/path-safety.cjs +7 -3
  22. package/dist/path-safety.cjs.map +1 -1
  23. package/dist/path-safety.js +8 -4
  24. package/dist/path-safety.js.map +1 -1
  25. package/dist/sandbox/bwrap.d.cts +75 -0
  26. package/dist/sandbox/bwrap.d.ts +75 -0
  27. package/dist/sandbox/index.cjs +473 -12
  28. package/dist/sandbox/index.cjs.map +1 -1
  29. package/dist/sandbox/index.d.cts +3 -0
  30. package/dist/sandbox/index.d.ts +3 -0
  31. package/dist/sandbox/index.js +456 -14
  32. package/dist/sandbox/index.js.map +1 -1
  33. package/dist/sandbox/linux-sandbox.d.cts +88 -0
  34. package/dist/sandbox/linux-sandbox.d.ts +88 -0
  35. package/dist/sandbox/seccomp.d.cts +10 -0
  36. package/dist/sandbox/seccomp.d.ts +10 -0
  37. package/dist/skills.cjs +7 -3
  38. package/dist/skills.cjs.map +1 -1
  39. package/dist/skills.js +7 -3
  40. package/dist/skills.js.map +1 -1
  41. package/dist/workflow.cjs.map +1 -1
  42. package/dist/workflow.js.map +1 -1
  43. package/package.json +2 -2
@@ -1,8 +1,236 @@
1
- import { execFile } from 'child_process';
1
+ import { execFileSync, execFile } from 'child_process';
2
+ import { existsSync, mkdtempSync, writeFileSync, rmSync } from 'fs';
3
+ import path, { dirname, join } from 'path';
4
+ import { tmpdir } from 'os';
2
5
  import { mkdir, writeFile } from 'fs/promises';
3
- import { dirname } from 'path';
4
6
 
5
- // src/sandbox/local-sandbox.ts
7
+ // src/sandbox/bwrap.ts
8
+ function buildBwrapArgv(mode, opts) {
9
+ if (mode === "danger-full-access") return null;
10
+ const cwd = path.resolve(opts.cwd);
11
+ const gitDir = path.join(cwd, ".git");
12
+ const hasGit = opts.gitDirExists ?? existsSync(gitDir);
13
+ const argv = [
14
+ // core, always (bwrap.rs:318-332; user+pid namespaces explicit so it works as root in containers)
15
+ "--new-session",
16
+ "--die-with-parent",
17
+ "--unshare-user",
18
+ "--unshare-pid",
19
+ // full-read filesystem base (bwrap.rs:446-452)
20
+ "--ro-bind",
21
+ "/",
22
+ "/",
23
+ "--dev",
24
+ "/dev",
25
+ "--proc",
26
+ "/proc"
27
+ ];
28
+ if (!opts.network) argv.push("--unshare-net");
29
+ if (opts.env) argv.push("--clearenv");
30
+ const setenv = {
31
+ ...opts.env ?? {},
32
+ // the flag signals the child that network is unshared (spawn.rs:20,79)
33
+ ...opts.network ? {} : { CODEX_SANDBOX_NETWORK_DISABLED: "1" }
34
+ };
35
+ for (const [k, v] of Object.entries(setenv)) argv.push("--setenv", k, v);
36
+ if (mode === "workspace-write") {
37
+ argv.push("--bind", cwd, cwd, "--bind", "/tmp", "/tmp");
38
+ if (hasGit) argv.push("--ro-bind", gitDir, gitDir);
39
+ }
40
+ argv.push("--chdir", cwd, "--");
41
+ return argv;
42
+ }
43
+ function detectBwrap(probes = realProbes) {
44
+ try {
45
+ const bin = probes.which();
46
+ if (!bin) return { ok: false, reason: "bwrap not found in PATH" };
47
+ const help = probes.helpText(bin);
48
+ if (!help?.includes("--perms")) {
49
+ return { ok: false, reason: `bwrap at ${bin} lacks --perms support (too old)` };
50
+ }
51
+ if (!probes.userns(bin)) {
52
+ return { ok: false, reason: "user namespaces unavailable (container/kernel restriction)" };
53
+ }
54
+ return { ok: true, bin };
55
+ } catch (err) {
56
+ return {
57
+ ok: false,
58
+ reason: `bwrap probe failed: ${err instanceof Error ? err.message : String(err)}`
59
+ };
60
+ }
61
+ }
62
+ var sondagensReais = 0;
63
+ function realProbeCount() {
64
+ return sondagensReais;
65
+ }
66
+ var realProbes = {
67
+ which: () => {
68
+ sondagensReais++;
69
+ try {
70
+ const out = execFileSync("which", ["bwrap"], { encoding: "utf8", timeout: 2e3 }).trim();
71
+ if (!out || out.startsWith(process.cwd() + path.sep)) return null;
72
+ return out;
73
+ } catch {
74
+ return null;
75
+ }
76
+ },
77
+ helpText: (bin) => {
78
+ try {
79
+ return execFileSync(bin, ["--help"], { encoding: "utf8", timeout: 2e3 });
80
+ } catch (err) {
81
+ const e = err;
82
+ return [e.stdout, e.stderr].filter(Boolean).join("\n") || null;
83
+ }
84
+ },
85
+ userns: (bin) => {
86
+ try {
87
+ execFileSync(bin, ["--unshare-user", "--unshare-net", "--ro-bind", "/", "/", "/bin/true"], {
88
+ timeout: 500,
89
+ stdio: "ignore"
90
+ });
91
+ return true;
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+ };
97
+ var memo;
98
+ function detectBwrapMemoizado(probes = realProbes) {
99
+ memo ??= detectBwrap(probes);
100
+ if (memo.ok && !existsSync(memo.bin)) {
101
+ memo = { ok: false, reason: `bwrap disappeared from ${memo.bin} after detection` };
102
+ }
103
+ return memo;
104
+ }
105
+ function resetBwrapMemo() {
106
+ memo = void 0;
107
+ }
108
+
109
+ // src/internal/security/redact.ts
110
+ var REDACT_ENABLED = readEnvOnce();
111
+ function readEnvOnce() {
112
+ const raw = process.env.THEOKIT_REDACT_SECRETS;
113
+ if (raw === void 0) return true;
114
+ return ["1", "true", "yes", "on"].includes(raw.toLowerCase());
115
+ }
116
+ var warnedOptOut = false;
117
+ if (!REDACT_ENABLED && !warnedOptOut) {
118
+ process.stderr.write(
119
+ "[theokit-sdk] Secret redaction is DISABLED via THEOKIT_REDACT_SECRETS. Credentials may leak into errors, telemetry, logs, transcripts.\n"
120
+ );
121
+ warnedOptOut = true;
122
+ }
123
+ var BUILTIN_PATTERNS = [
124
+ // T5.4: 30+ vendor prefixes (was 12 pre-T5.4). Order matters — more
125
+ // specific prefixes precede generic ones (e.g., sk-ant-admin01 before
126
+ // sk-ant-, sk-proj- before sk-). PEM block deliberately first so its
127
+ // multi-line span runs before any per-line patterns can fire.
128
+ /-----BEGIN[ ]+(?:RSA |EC |DSA |OPENSSH |ENCRYPTED |)PRIVATE KEY-----[\s\S]+?-----END[ ]+(?:RSA |EC |DSA |OPENSSH |ENCRYPTED |)PRIVATE KEY-----/g,
129
+ // JWT — exact 3-segment base64url. Dotted; the body floor of 4 chars per
130
+ // segment matches the minimum legal payload while skipping `a.b.c` noise.
131
+ /eyJ[A-Za-z0-9_-]{4,}\.eyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}/g,
132
+ // Azure Storage SAS — match the sig= component (URL-encoded base64).
133
+ /(?<=[?&]sig=)[A-Za-z0-9%+/]{20,}/g,
134
+ // Anthropic
135
+ /sk-ant-admin01-[A-Za-z0-9_-]{10,}/g,
136
+ // Anthropic admin keys (must precede sk-ant-)
137
+ /sk-ant-[A-Za-z0-9_-]{10,}/g,
138
+ // Anthropic regular
139
+ // OpenAI family + clones (sk- generic must come AFTER all sk-foo- variants)
140
+ /sk-proj-[A-Za-z0-9_-]{10,}/g,
141
+ // OpenAI project key (must precede sk- generic)
142
+ /sk-[A-Za-z0-9_-]{10,}/g,
143
+ // OpenAI / OpenRouter / DeepInfra / Together / DeepSeek
144
+ // Provider prefixes (alphabetized for maintainability)
145
+ /AIza[A-Za-z0-9_-]{35}/g,
146
+ // Google API key
147
+ /AKIA[A-Z0-9]{16}/g,
148
+ // AWS access key
149
+ /fw_[A-Za-z0-9]{20,}/g,
150
+ // Fireworks
151
+ /glpat-[A-Za-z0-9_-]{20}/g,
152
+ // GitLab PAT
153
+ /ghp_[A-Za-z0-9]{36}/g,
154
+ // GitHub PAT classic
155
+ /github_pat_[A-Za-z0-9_]{82}/g,
156
+ // GitHub PAT fine-grained
157
+ /gsk_[A-Za-z0-9]{20,}/g,
158
+ // Groq
159
+ /hf_[A-Za-z0-9]{20,}/g,
160
+ // HuggingFace
161
+ /\bpa-[A-Za-z0-9_-]{20,}/g,
162
+ // Voyage AI (word-boundary to skip CSS / kebab IDs)
163
+ /pcsk_[A-Za-z0-9_-]{20,}/g,
164
+ // Pinecone
165
+ /pplx-[A-Za-z0-9_-]{20,}/g,
166
+ // Perplexity
167
+ /r8_[A-Za-z0-9_-]{20,}/g,
168
+ // Replicate
169
+ /rk_live_[A-Za-z0-9]{20,}/g,
170
+ // Stripe restricted
171
+ /sk_live_[A-Za-z0-9]{20,}/g,
172
+ // Stripe secret
173
+ /sntrys_[A-Za-z0-9]{40,}/g,
174
+ // Sentry user auth
175
+ /xai-[A-Za-z0-9_-]{20,}/g,
176
+ // xAI (Grok)
177
+ /xox[bpasr]-[A-Za-z0-9-]{10,}/g,
178
+ //Slack tokens
179
+ // Additional unique-prefix tokens with low false-positive risk
180
+ /npm_[A-Za-z0-9]{36}/g,
181
+ // npm access token
182
+ /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g,
183
+ // SendGrid
184
+ /\bSK[A-Za-z0-9]{32}\b/g,
185
+ // Twilio API SID (word-boundary to skip CSS class noise)
186
+ /\bkey-[a-f0-9]{32}\b/g,
187
+ // Mailgun (hex-only narrows false positives)
188
+ /MT[A-Za-z0-9_-]{23}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}/g,
189
+ // Discord bot
190
+ /\b(?:sdk|mob)-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b/g
191
+ // LaunchDarkly
192
+ ];
193
+ var BEARER_PATTERN = /\b(Bearer\s+)([A-Za-z0-9_\-.+/=]{8,})/g;
194
+ var PARAM_PATTERN = /(\b(?:access_token|api_key|api-key|client_secret|credential|credentials|id_token|jwt|password|private_key|refresh_token|secret|service_account|session_token|token|x-api-key)\b["']?\s*[:=]\s*["']?)([A-Za-z0-9_\-.+/]+)/gi;
195
+ var _extraPatterns = [];
196
+ function maskToken(token) {
197
+ if (token.length < 18) return "***";
198
+ return `${token.slice(0, 6)}...${token.slice(-4)}`;
199
+ }
200
+ var MASK_SHAPE = /^.{6}\.\.\..{4}$/s;
201
+ function coerceToString(value) {
202
+ if (typeof value === "string") return value;
203
+ if (value === null || value === void 0) return null;
204
+ if (typeof value === "object") {
205
+ try {
206
+ const s = JSON.stringify(value);
207
+ return s === void 0 ? null : s;
208
+ } catch {
209
+ return "[unredactable: circular]";
210
+ }
211
+ }
212
+ return String(value);
213
+ }
214
+ function redactSecrets(text, opts) {
215
+ const coerced = coerceToString(text);
216
+ if (coerced === null) return "";
217
+ if (!REDACT_ENABLED) return coerced;
218
+ let s = coerced;
219
+ for (const re of BUILTIN_PATTERNS) {
220
+ s = s.replace(re, (m) => maskToken(m));
221
+ }
222
+ for (const re of _extraPatterns) {
223
+ s = s.replace(re, (m) => maskToken(m));
224
+ }
225
+ {
226
+ s = s.replace(BEARER_PATTERN, (_, prefix) => `${prefix}***`);
227
+ s = s.replace(PARAM_PATTERN, (whole, prefix, value) => {
228
+ if (MASK_SHAPE.test(value)) return whole;
229
+ return `${prefix}***`;
230
+ });
231
+ }
232
+ return s;
233
+ }
6
234
 
7
235
  // src/internal/runtime/lifecycle/env-policy.ts
8
236
  var SECRET_PATTERNS = [
@@ -89,15 +317,15 @@ var SandboxBackend = class {
89
317
  env: config.env ?? "inherit-scrubbed"
90
318
  };
91
319
  }
92
- async readFile(path) {
93
- const result = await this.execute(`cat ${this.shellEscape(path)}`);
320
+ async readFile(path2) {
321
+ const result = await this.execute(`cat ${this.shellEscape(path2)}`);
94
322
  if (result.exitCode !== 0) {
95
323
  throw new Error(`readFile failed: ${result.stderr}`);
96
324
  }
97
325
  return result.stdout;
98
326
  }
99
- async writeFile(path, content) {
100
- await this.uploadFile(path, content);
327
+ async writeFile(path2, content) {
328
+ await this.uploadFile(path2, content);
101
329
  }
102
330
  async glob(pattern, cwd) {
103
331
  const dir = cwd ?? this.config.workDir ?? ".";
@@ -107,16 +335,16 @@ var SandboxBackend = class {
107
335
  if (result.exitCode !== 0) return [];
108
336
  return result.stdout.trim().split("\n").filter(Boolean);
109
337
  }
110
- async grep(pattern, path) {
111
- const target = path ?? ".";
338
+ async grep(pattern, path2) {
339
+ const target = path2 ?? ".";
112
340
  const result = await this.execute(
113
341
  `grep -rn ${this.shellEscape(pattern)} ${this.shellEscape(target)} 2>/dev/null`
114
342
  );
115
343
  if (result.exitCode !== 0) return [];
116
344
  return result.stdout.trim().split("\n").filter(Boolean);
117
345
  }
118
- async listDir(path) {
119
- const result = await this.execute(`ls -1 ${this.shellEscape(path)}`);
346
+ async listDir(path2) {
347
+ const result = await this.execute(`ls -1 ${this.shellEscape(path2)}`);
120
348
  if (result.exitCode !== 0) return [];
121
349
  return result.stdout.trim().split("\n").filter(Boolean);
122
350
  }
@@ -174,13 +402,227 @@ var LocalSandbox = class extends SandboxBackend {
174
402
  timedOut
175
403
  };
176
404
  }
177
- async uploadFile(path, content) {
178
- const fullPath = path.startsWith("/") ? path : `${this.config.workDir}/${path}`;
405
+ async uploadFile(path2, content) {
406
+ const fullPath = path2.startsWith("/") ? path2 : `${this.config.workDir}/${path2}`;
179
407
  await mkdir(dirname(fullPath), { recursive: true });
180
408
  await writeFile(fullPath, content, "utf-8");
181
409
  }
182
410
  };
183
411
 
412
+ // src/sandbox/seccomp.ts
413
+ var BPF_LD = 0;
414
+ var BPF_W = 0;
415
+ var BPF_ABS = 32;
416
+ var BPF_JMP = 5;
417
+ var BPF_JEQ = 16;
418
+ var BPF_JGE = 48;
419
+ var BPF_K = 0;
420
+ var BPF_RET = 6;
421
+ var LD_ABS_W = BPF_LD | BPF_W | BPF_ABS;
422
+ var JEQ_K = BPF_JMP | BPF_JEQ | BPF_K;
423
+ var JGE_K = BPF_JMP | BPF_JGE | BPF_K;
424
+ var RET_K = BPF_RET | BPF_K;
425
+ var OFF_NR = 0;
426
+ var OFF_ARCH = 4;
427
+ var OFF_ARG0 = 16;
428
+ var AUDIT_ARCH_X86_64 = 3221225534;
429
+ var X32_BIT = 1073741824;
430
+ var SECCOMP_RET_ALLOW = 2147418112;
431
+ var SECCOMP_RET_ERRNO = 327680;
432
+ var EPERM = 1;
433
+ var RET_EPERM = SECCOMP_RET_ERRNO | EPERM & 65535;
434
+ var SECCOMP_RET_KILL_PROCESS = 2147483648;
435
+ var AF_UNIX = 1;
436
+ var ALWAYS_DENIED = [101, 310, 311, 425, 426, 427];
437
+ var NETWORK_DENIED = [42, 43, 288, 49, 50, 52, 51, 48, 44, 307, 299, 55, 54];
438
+ var SOCKET_SYSCALLS = [41, 53];
439
+ var stmt = (code, k) => ({ code, jt: 0, jf: 0, k });
440
+ var jmp = (code, k, jt, jf) => ({ code, jt, jf, k });
441
+ function buildSeccompFilter(opts) {
442
+ const insns = [];
443
+ const ALLOW = /* @__PURE__ */ Symbol("ALLOW");
444
+ const DENY = /* @__PURE__ */ Symbol("DENY");
445
+ const KILL = /* @__PURE__ */ Symbol("KILL");
446
+ const body = [];
447
+ const push = (code, k, jt = 0, jf = 0) => {
448
+ body.push({ code, k, jt, jf });
449
+ };
450
+ push(LD_ABS_W, OFF_ARCH);
451
+ push(JEQ_K, AUDIT_ARCH_X86_64, 0, KILL);
452
+ push(LD_ABS_W, OFF_NR);
453
+ push(JGE_K, X32_BIT, KILL, 0);
454
+ const denied = opts.networkRestricted ? [...ALWAYS_DENIED, ...NETWORK_DENIED] : [...ALWAYS_DENIED];
455
+ for (const nr of denied) push(JEQ_K, nr, DENY, 0);
456
+ if (opts.networkRestricted) {
457
+ for (const sysno of SOCKET_SYSCALLS) {
458
+ push(JEQ_K, sysno, 0, 2);
459
+ push(LD_ABS_W, OFF_ARG0);
460
+ push(JEQ_K, AF_UNIX, ALLOW, DENY);
461
+ }
462
+ }
463
+ push(RET_K, SECCOMP_RET_ALLOW);
464
+ const allowIdx = body.length - 1;
465
+ push(RET_K, RET_EPERM);
466
+ const denyIdx = body.length - 1;
467
+ push(RET_K, SECCOMP_RET_KILL_PROCESS);
468
+ const killIdx = body.length - 1;
469
+ const resolve = (i, t) => {
470
+ const abs = t === ALLOW ? allowIdx : t === DENY ? denyIdx : t === KILL ? killIdx : i + 1 + t;
471
+ const off = abs - (i + 1);
472
+ if (off < 0 || off > 255) throw new RangeError(`seccomp jump out of range at ${i}: ${off}`);
473
+ return off;
474
+ };
475
+ for (const [i, b] of body.entries()) {
476
+ if (b.code === JEQ_K || b.code === JGE_K) {
477
+ insns.push(jmp(b.code, b.k, resolve(i, b.jt), resolve(i, b.jf)));
478
+ } else {
479
+ insns.push(stmt(b.code, b.k));
480
+ }
481
+ }
482
+ const buf = Buffer.alloc(insns.length * 8);
483
+ insns.forEach((ins, i) => {
484
+ buf.writeUInt16LE(ins.code, i * 8);
485
+ buf.writeUInt8(ins.jt, i * 8 + 2);
486
+ buf.writeUInt8(ins.jf, i * 8 + 3);
487
+ buf.writeUInt32LE(ins.k >>> 0, i * 8 + 4);
488
+ });
489
+ return buf;
490
+ }
491
+
492
+ // src/sandbox/linux-sandbox.ts
493
+ function shellQuote(s) {
494
+ return `'${s.replaceAll("'", `'\\''`)}'`;
495
+ }
496
+ function wrapCommandForSandbox(mode, opts, command) {
497
+ const argv = buildBwrapArgv(mode, { cwd: opts.cwd, network: opts.network, env: opts.env });
498
+ if (argv === null) return null;
499
+ const bin = opts.bin ?? "bwrap";
500
+ const seccompArgv = opts.seccompPath !== void 0 ? ["--seccomp", "3"] : [];
501
+ const base = `${shellQuote(bin)} ${[...argv.slice(0, -1), ...seccompArgv, "--"].map(shellQuote).join(" ")} /bin/sh -c ${shellQuote(command)}`;
502
+ return opts.seccompPath !== void 0 ? `${base} 3< ${shellQuote(opts.seccompPath)}` : base;
503
+ }
504
+ var ENV_ALLOWLIST = [
505
+ "PATH",
506
+ "HOME",
507
+ "LANG",
508
+ "LC_ALL",
509
+ "LC_CTYPE",
510
+ "TERM",
511
+ "USER",
512
+ "TMPDIR",
513
+ "SHELL"
514
+ ];
515
+ function allowlistedEnv(source = process.env) {
516
+ const out = {};
517
+ for (const k of ENV_ALLOWLIST) {
518
+ const v = source[k];
519
+ if (v !== void 0) out[k] = v;
520
+ }
521
+ return out;
522
+ }
523
+ var LinuxSandbox = class extends LocalSandbox {
524
+ mode;
525
+ network;
526
+ cwd;
527
+ bin;
528
+ env;
529
+ /** M63 — path to the cBPF seccomp program written host-side; passed to `bwrap --seccomp 3` via a
530
+ * shell redirect. `undefined` when the network is unrestricted OR generation failed (honest fallback). */
531
+ seccompPath;
532
+ constructor(config, opts) {
533
+ super(config);
534
+ this.mode = opts.mode;
535
+ this.network = opts.network ?? false;
536
+ this.cwd = config.workDir ?? process.cwd();
537
+ this.bin = opts.bin ?? "bwrap";
538
+ this.env = opts.env ?? allowlistedEnv();
539
+ this.seccompPath = this.network ? void 0 : restrictedSeccompPath();
540
+ }
541
+ /** Extracted for test visibility — delegates to the pure `wrapCommandForSandbox` (M57, single wrap SoT). */
542
+ wrapCommand(command) {
543
+ return wrapCommandForSandbox(
544
+ this.mode,
545
+ {
546
+ cwd: this.cwd,
547
+ network: this.network,
548
+ env: this.env,
549
+ bin: this.bin,
550
+ seccompPath: this.seccompPath
551
+ },
552
+ command
553
+ );
554
+ }
555
+ execute(command, opts) {
556
+ const wrapped = this.wrapCommand(command);
557
+ if (wrapped === null) return super.execute(command, opts);
558
+ return super.execute(wrapped, opts);
559
+ }
560
+ };
561
+ var warnedNonX64 = false;
562
+ function seccompPathForArch(arch, warn) {
563
+ if (arch !== "x64") {
564
+ if (!warnedNonX64) {
565
+ warnedNonX64 = true;
566
+ warn(
567
+ `[sandbox] seccomp syscall filter unsupported on ${arch} (x86_64 only in v1) \u2014 running without the filter; bwrap FS/network confinement still applies.`
568
+ );
569
+ }
570
+ return void 0;
571
+ }
572
+ try {
573
+ const dir = mkdtempSync(join(tmpdir(), "ab-seccomp-"));
574
+ const path2 = join(dir, "filter.bpf");
575
+ writeFileSync(path2, buildSeccompFilter({ networkRestricted: true }));
576
+ const cleanup = () => rmSync(dir, { recursive: true, force: true });
577
+ process.once("exit", cleanup);
578
+ process.once("SIGINT", cleanup);
579
+ process.once("SIGTERM", cleanup);
580
+ return path2;
581
+ } catch (err) {
582
+ warn(
583
+ `[sandbox] seccomp filter unavailable (${err instanceof Error ? err.message : String(err)}) \u2014 running without syscall filter (bwrap FS/network confinement still applies).`
584
+ );
585
+ return void 0;
586
+ }
587
+ }
588
+ var seccompFilterPath;
589
+ function restrictedSeccompPath() {
590
+ if (seccompFilterPath !== void 0) return seccompFilterPath ?? void 0;
591
+ const path2 = seccompPathForArch(process.arch, (m) => console.warn(redactSecrets(m)));
592
+ seccompFilterPath = path2 ?? null;
593
+ return path2;
594
+ }
595
+ var warnedUnavailable = false;
596
+ function resetSandboxWarnLatch() {
597
+ warnedUnavailable = false;
598
+ }
599
+ function resolveSandboxPosture(opts) {
600
+ if (opts.mode === "danger-full-access") {
601
+ return { mode: opts.mode, enforced: false, detail: "no confinement (danger-full-access)" };
602
+ }
603
+ const detection = (opts.detect ?? detectBwrapMemoizado)();
604
+ if (!detection.ok) {
605
+ return { mode: opts.mode, enforced: false, detail: `tool-gating only \u2014 ${detection.reason}` };
606
+ }
607
+ return { mode: opts.mode, enforced: true, detail: "kernel (bwrap)" };
608
+ }
609
+ function createSandboxBackend(opts) {
610
+ const config = { workDir: opts.workDir, timeoutMs: opts.timeoutMs };
611
+ if (opts.mode === "danger-full-access") return new LocalSandbox(config);
612
+ const detection = (opts.detect ?? detectBwrapMemoizado)();
613
+ if (!detection.ok) {
614
+ if (!warnedUnavailable) {
615
+ warnedUnavailable = true;
616
+ const warn = opts.warn ?? ((m) => console.warn(redactSecrets(m)));
617
+ warn(
618
+ `[sandbox] OS-level enforcement unavailable (${detection.reason}) \u2014 falling back to tool-level gating only (sandbox_mode=${opts.mode}).`
619
+ );
620
+ }
621
+ return new LocalSandbox(config);
622
+ }
623
+ return new LinuxSandbox(config, { mode: opts.mode, network: opts.network, bin: detection.bin });
624
+ }
625
+
184
626
  // src/errors.ts
185
627
  var TheokitAgentError = class extends Error {
186
628
  name = "TheokitAgentError";
@@ -246,6 +688,6 @@ async function provisionRepo(sandboxOrOpts, maybeOpts) {
246
688
  return { repoDir };
247
689
  }
248
690
 
249
- export { LocalSandbox, RepoProvisionError, SandboxBackend, SandboxNotAvailableError, SandboxSecurityError, provisionRepo, resolveSandbox };
691
+ export { LinuxSandbox, LocalSandbox, RepoProvisionError, SandboxBackend, SandboxNotAvailableError, SandboxSecurityError, allowlistedEnv, buildBwrapArgv, buildSeccompFilter, createSandboxBackend, detectBwrap, detectBwrapMemoizado, provisionRepo, realProbeCount, realProbes, resetBwrapMemo, resetSandboxWarnLatch, resolveSandbox, resolveSandboxPosture, restrictedSeccompPath, seccompPathForArch, wrapCommandForSandbox };
250
692
  //# sourceMappingURL=index.js.map
251
693
  //# sourceMappingURL=index.js.map