seisin 0.6.5 → 0.7.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.
- package/README.md +22 -15
- package/package.json +4 -4
- package/src/cli.js +4 -4
- package/src/commands/check.js +2 -2
- package/src/config.js +1 -1
- package/src/inspect/inject.js +5 -3
- package/src/keys.js +5 -3
- package/src/sandboxes/index.js +26 -9
- package/src/sandboxes/srt.js +27 -7
- package/src/sandboxes/tartaros.js +7 -5
package/README.md
CHANGED
|
@@ -168,12 +168,14 @@ own fixtures:
|
|
|
168
168
|
|
|
169
169
|
Others are the sandbox's, and differ between the two built in:
|
|
170
170
|
|
|
171
|
-
| | `
|
|
171
|
+
| | `tartaros` (the default) | `srt` |
|
|
172
172
|
|---|---|---|
|
|
173
|
-
| a process the agent leaves running | **macOS:**
|
|
174
|
-
| deleting inside the territory | write includes delete; the ask is upstream as [issue #545](https://github.com/anthropics/sandbox-runtime/issues/545) |
|
|
175
|
-
| `[network] deny_addresses` |
|
|
176
|
-
|
|
|
173
|
+
| a process the agent leaves running | **macOS:** the run's process group is killed; one that calls `setsid` outlives it (measured 2026-10-16). **Linux:** the run's cgroup is killed whole (read from its source) | **macOS:** it outlives the run, whether it double-forks, ignores signals under `nohup` or calls `setsid` (measured 2026-10-16). **Linux:** the PID namespace ends with the run (read from its source) |
|
|
174
|
+
| deleting inside the territory | not measured here | write includes delete; the ask is upstream as [issue #545](https://github.com/anthropics/sandbox-runtime/issues/545) |
|
|
175
|
+
| `[network] deny_addresses` | not yet: a role that has it runs under `srt` where it is installed, and `check` says why | enforced |
|
|
176
|
+
| `local_binding = true` | refused (it grants ports one at a time): a role that has it runs under `srt` where it is installed | enforced |
|
|
177
|
+
| `local_ports`, a client that skips the proxy (a database driver) | reaches the listed port: the kernel grants it | denied: only a client that honours the proxy gets there |
|
|
178
|
+
| what it is | by the author of seisin, alpha | Anthropic's, described by its authors as a research preview |
|
|
177
179
|
|
|
178
180
|
`seisin check` says which sandbox each role gets before anything runs.
|
|
179
181
|
|
|
@@ -196,18 +198,22 @@ or more.
|
|
|
196
198
|
npm install -g seisin
|
|
197
199
|
```
|
|
198
200
|
|
|
199
|
-
That pulls in [
|
|
201
|
+
That pulls in [tartaros](https://github.com/carlostapiacl/tartaros), the default sandbox, prebuilt for macOS and Linux: Seatbelt on macOS, Landlock and seccomp on Linux. No containers, no VMs, no daemon, and no other dependency. [`@anthropic-ai/sandbox-runtime`](https://github.com/anthropics/sandbox-runtime) (`srt`) is used where you install it apart, after tartaros: for a role tartaros cannot hold (a blanket `local_binding`, `deny_addresses`, a Linux host without Landlock), or anywhere `[sandbox] provider` names it. A sandbox of your own can enforce too ([docs/sandboxes.md](https://github.com/carlostapiacl/seisin/blob/main/docs/sandboxes.md)).
|
|
200
202
|
|
|
201
|
-
|
|
203
|
+
```bash
|
|
204
|
+
npm install -g @anthropic-ai/sandbox-runtime@0.0.81 # optional: srt, for what tartaros cannot hold
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**For srt on Linux you also need three system packages.** Without them srt refuses to start
|
|
202
208
|
rather than run unconfined:
|
|
203
209
|
|
|
204
210
|
```bash
|
|
205
211
|
apt install bubblewrap ripgrep socat # or your distro's equivalent
|
|
206
212
|
```
|
|
207
213
|
|
|
208
|
-
**On Ubuntu 24.04 and newer**, unprivileged user namespaces are off by default
|
|
209
|
-
|
|
210
|
-
namespace`:
|
|
214
|
+
**On Ubuntu 24.04 and newer**, unprivileged user namespaces are off by default. tartaros
|
|
215
|
+
runs without them (with the limits in its docs/limits.md); bubblewrap, and so srt, needs one.
|
|
216
|
+
Every srt run dies with `bwrap: No permissions to create new namespace`:
|
|
211
217
|
|
|
212
218
|
```bash
|
|
213
219
|
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
|
@@ -472,8 +478,8 @@ its own situation. **[The log, `review`, the console, the MCP server and walls
|
|
|
472
478
|
explanation — never the boundary
|
|
473
479
|
```
|
|
474
480
|
|
|
475
|
-
|
|
476
|
-
(`
|
|
481
|
+
tartaros is the default enforcer, not the only one. `[sandbox] provider` picks another
|
|
482
|
+
(`srt`, or any sandbox declared as a command) per role, and only where it enforces the whole
|
|
477
483
|
policy: see [docs/sandboxes.md](https://github.com/carlostapiacl/seisin/blob/main/docs/sandboxes.md).
|
|
478
484
|
|
|
479
485
|
That split keeps seisin small. Because the kernel is the boundary, seisin does not have to be airtight, only *legible*. A leaky explainer costs you a confusing log line. A leaky enforcer costs you the repo.
|
|
@@ -508,7 +514,8 @@ writable: each run gets a temp directory of its own instead.
|
|
|
508
514
|
|
|
509
515
|
## Prior art
|
|
510
516
|
|
|
511
|
-
[
|
|
517
|
+
[tartaros](https://github.com/carlostapiacl/tartaros) is the enforcement seisin sits on, and
|
|
518
|
+
[`sandbox-runtime`](https://github.com/anthropics/sandbox-runtime) the one it was built on.
|
|
512
519
|
OpenShell, hazmat and nono isolate the agent from your machine; containers and microVMs isolate a
|
|
513
520
|
process from the host. seisin separates roles from each other inside one repository, and names the
|
|
514
521
|
owner when the kernel denies something. They compose: the agent seisin confines can run inside any
|
|
@@ -517,8 +524,8 @@ of them. **[Each one, and why not a container →](https://github.com/carlostapi
|
|
|
517
524
|
## Status
|
|
518
525
|
|
|
519
526
|
CI runs the full suite on macOS and `ubuntu-latest`, Node 18/20/22/24, on every pull request and
|
|
520
|
-
push to `main`. On 2026-11-
|
|
521
|
-
Linux **
|
|
527
|
+
push to `main`. On 2026-11-04, on Node 24: macOS **1134 tests, 1125 passing, 9 skipped**;
|
|
528
|
+
Linux **1133, 1104 passing, 29 skipped**. The skips are platform tests; the job fails if the 26
|
|
522
529
|
core boundary tests in `test/sandbox.test.js` skip. A release is published only from CI, after
|
|
523
530
|
that passes, with npm provenance. [Which claim was measured where](https://github.com/carlostapiacl/seisin/blob/main/docs/what-it-has-been-put-through.md#where-each-claim-was-actually-run),
|
|
524
531
|
and [what running agents behind it cost](https://github.com/carlostapiacl/seisin/blob/main/docs/field-notes.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seisin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A permission layer for AI coding agents: role-scoped folders and keys, enforced by the OS, and when it denies something it tells you whose file it was.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"node": ">=18"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@
|
|
45
|
+
"@carlostapiacl/tartaros": "0.1.0-alpha.23"
|
|
46
46
|
},
|
|
47
|
-
"
|
|
48
|
-
"@
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@anthropic-ai/sandbox-runtime": "0.0.81"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"test": "node --test test/*.test.js",
|
package/src/cli.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* `commands/` and every decision lives in a module beside it, so that a change
|
|
8
8
|
* to how something is printed cannot change what is allowed.
|
|
9
9
|
*
|
|
10
|
-
* Enforcement is not ours.
|
|
11
|
-
* system — Seatbelt on macOS,
|
|
12
|
-
* care how a command was spelled. What seisin adds is the part a kernel can
|
|
10
|
+
* Enforcement is not ours. The sandbox (tartaros by default, or
|
|
11
|
+
* sandbox-runtime) asks the operating system — Seatbelt on macOS, Landlock or
|
|
12
|
+
* bubblewrap on Linux — and the kernel does not care how a command was spelled. What seisin adds is the part a kernel can
|
|
13
13
|
* never know: which role a path belongs to, and therefore who to ask next.
|
|
14
14
|
*/
|
|
15
15
|
import { readFileSync } from "node:fs";
|
|
@@ -105,7 +105,7 @@ Any command takes --policy <file> (the policy, rooted at the working directory)
|
|
|
105
105
|
both outside the repo (docs/runs.md).
|
|
106
106
|
seisin help limits says what seisin cannot do, in any repo.
|
|
107
107
|
|
|
108
|
-
Enforcement comes from the sandbox (
|
|
108
|
+
Enforcement comes from the sandbox (tartaros by default, or sandbox-runtime), which asks the OS.
|
|
109
109
|
seisin decides what to ask for, and says whose file it was when the answer is no.
|
|
110
110
|
`;
|
|
111
111
|
|
package/src/commands/check.js
CHANGED
|
@@ -11,7 +11,7 @@ const say = out;
|
|
|
11
11
|
import { measureSystemLog } from "../violations.js";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { settingsFor } from "../srt-settings.js";
|
|
14
|
-
import { weigh } from "../sandboxes/index.js";
|
|
14
|
+
import { weigh, DEFAULT_PROVIDERS } from "../sandboxes/index.js";
|
|
15
15
|
import { runsRoot } from "../rundir.js";
|
|
16
16
|
import { channelName } from "../spool.js";
|
|
17
17
|
|
|
@@ -81,7 +81,7 @@ export function sandboxes(config, only = null, { platform = process.platform, qu
|
|
|
81
81
|
const out = quiet ? () => {} : say;
|
|
82
82
|
const sock = join(runsRoot(), "check", channelName(platform));
|
|
83
83
|
const rows = [];
|
|
84
|
-
out(`\n${C.b}sandboxes${C.off} (${(config.sandbox?.provider ??
|
|
84
|
+
out(`\n${C.b}sandboxes${C.off} (${(config.sandbox?.provider ?? DEFAULT_PROVIDERS).join(" → ")})\n`);
|
|
85
85
|
for (const r of Object.values(config.roles)) {
|
|
86
86
|
if (only && r.name !== only) continue;
|
|
87
87
|
let settings;
|
package/src/config.js
CHANGED
|
@@ -526,7 +526,7 @@ const BUILTIN_SANDBOXES = ["srt", "tartaros"];
|
|
|
526
526
|
* `[sandbox]`: which sandbox enforces the roles, and any declared by command.
|
|
527
527
|
*
|
|
528
528
|
* `provider` is one name or a list in order of preference; absent, it is
|
|
529
|
-
* `srt
|
|
529
|
+
* `["tartaros", "srt"]` (DEFAULT_PROVIDERS; `srt` alone before 0.7). A name is one seisin ships or one declared under
|
|
530
530
|
* `[sandbox.providers.<name>]` — an unknown one is refused here, never read as
|
|
531
531
|
* the default, because "the config asks for tartaros and the run used srt"
|
|
532
532
|
* without a word is the silent substitution sandboxes/index.js exists to stop.
|
package/src/inspect/inject.js
CHANGED
|
@@ -29,15 +29,17 @@ export function injectWarnings(config, roles) {
|
|
|
29
29
|
const allowed = r.network ?? config.allowedDomains ?? [];
|
|
30
30
|
|
|
31
31
|
// Which sandbox masks is the sandbox's to say (sandboxes/, `masks`): on macOS
|
|
32
|
-
// only tartaros does
|
|
32
|
+
// only tartaros does without leaving the real value where the agent can read
|
|
33
|
+
// it, so a provider list without it cannot run the role here.
|
|
33
34
|
const names = sandboxesOf(config).map((s) => s.name);
|
|
34
35
|
if (process.platform === "darwin" && !names.includes("tartaros"))
|
|
35
36
|
out.push({
|
|
36
37
|
kind: "inject-platform",
|
|
37
38
|
role: r.name,
|
|
38
39
|
headline: `${r.name}: key_mode = "inject" needs tartaros on macOS — [sandbox] provider is ${names.join(", ")}`,
|
|
39
|
-
detail: `On macOS sandbox-runtime
|
|
40
|
-
`
|
|
40
|
+
detail: `On macOS sandbox-runtime masks credentials, but it holds the real value in its own environment, ` +
|
|
41
|
+
`which the agent can read from inside the sandbox (anthropics/sandbox-runtime#593 takes it out of ` +
|
|
42
|
+
`there). So this role will not run here. Add tartaros: [sandbox] provider = ["tartaros", "srt"]. Or run it on ` +
|
|
41
43
|
`Linux, or use "env"/"run-file" here.`,
|
|
42
44
|
});
|
|
43
45
|
else if (process.platform !== "linux" && process.platform !== "darwin")
|
package/src/keys.js
CHANGED
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
* policy holds a reviewable reference instead of a path, and every resolution
|
|
31
31
|
* is logged without its value (`event: "key"`, see commands/run.js) — but it is not "the agent never sees it". That
|
|
32
32
|
* is `inject`, which costs terminating the role's TLS with a CA of your own and
|
|
33
|
-
* needs a sandbox that masks credentials
|
|
34
|
-
*
|
|
33
|
+
* needs a sandbox that masks credentials without leaving the real value where
|
|
34
|
+
* the agent can read it: srt on Linux, tartaros on Linux and macOS (see
|
|
35
|
+
* `injectGate` below, and `masks` on each sandbox).
|
|
35
36
|
*
|
|
36
37
|
* ## Why the value is never written down
|
|
37
38
|
*
|
|
@@ -526,7 +527,8 @@ export function modeOf(config, role, entry) {
|
|
|
526
527
|
* the agent a sentinel and substitutes the real value on egress to the
|
|
527
528
|
* credential's declared hosts. Which sandbox can do that is the sandbox's to
|
|
528
529
|
* say (`masks` in sandboxes/): srt on Linux, tartaros on Linux and macOS
|
|
529
|
-
* (
|
|
530
|
+
* (on macOS srt masks, but holds the real value in an environment the agent can
|
|
531
|
+
* read; anthropics/sandbox-runtime#593 takes it out of there). A
|
|
530
532
|
* sandbox that does not mask is never handed the role, because the real value
|
|
531
533
|
* is in the environment it is started with. Here only the platforms where no
|
|
532
534
|
* sandbox masks are refused outright.
|
package/src/sandboxes/index.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* seisin decides the policy; a sandbox enforces it. There are three ways in:
|
|
5
5
|
*
|
|
6
|
-
* - `
|
|
7
|
-
*
|
|
6
|
+
* - `tartaros` (built in, the default, installed with seisin): reads the
|
|
7
|
+
* settings file itself (`--settings`);
|
|
8
|
+
* - `srt` (built in, installed apart): @anthropic-ai/sandbox-runtime;
|
|
8
9
|
* - any name declared under `[sandbox.providers.<name>]`: a command that
|
|
9
10
|
* takes the settings file (command.js, docs/sandboxes.md).
|
|
10
11
|
*
|
|
@@ -23,17 +24,25 @@
|
|
|
23
24
|
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
24
25
|
import { join } from "node:path";
|
|
25
26
|
import { tmpdir } from "node:os";
|
|
26
|
-
import { srt } from "./srt.js";
|
|
27
|
+
import { srt, SRT_PACKAGE, SRT_VERSION } from "./srt.js";
|
|
27
28
|
import { machOf } from "../browsers.js";
|
|
28
29
|
import { tartaros } from "./tartaros.js";
|
|
29
30
|
import { commandSandbox } from "./command.js";
|
|
30
31
|
|
|
31
32
|
export const BUILTIN_SANDBOXES = { srt, tartaros };
|
|
32
|
-
export const DEFAULT_SANDBOX = "
|
|
33
|
+
export const DEFAULT_SANDBOX = "tartaros";
|
|
34
|
+
/**
|
|
35
|
+
* Without `[sandbox] provider`: tartaros, which installs with seisin, then srt
|
|
36
|
+
* where it is installed apart (it is not a dependency since 0.7). A role
|
|
37
|
+
* tartaros cannot hold (a blanket local binding, a host's missing Landlock)
|
|
38
|
+
* runs under srt if it is there and holds all of it, and otherwise does not
|
|
39
|
+
* run, with what each one lacked.
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_PROVIDERS = [DEFAULT_SANDBOX, "srt"];
|
|
33
42
|
|
|
34
43
|
/** The sandboxes the config names, in its order, as provider objects. */
|
|
35
44
|
export function sandboxesOf(config) {
|
|
36
|
-
const names = config.sandbox?.provider ??
|
|
45
|
+
const names = config.sandbox?.provider ?? DEFAULT_PROVIDERS;
|
|
37
46
|
return names.map((n) => BUILTIN_SANDBOXES[n] ?? commandSandbox(n, config.sandbox.providers[n].command, config.sandbox.providers[n].check, { tagsDenials: config.sandbox.providers[n].tagsDenials }));
|
|
38
47
|
}
|
|
39
48
|
|
|
@@ -91,7 +100,15 @@ export function weigh(config, settings, { platform = process.platform, role = nu
|
|
|
91
100
|
const ruled = requests.length && !sandbox.requestRules
|
|
92
101
|
? [`requests (${requests.map((q) => q.host).join(", ")}): ${sandbox.name} cannot limit the methods and paths of a host from its settings, tartaros can`]
|
|
93
102
|
: [];
|
|
94
|
-
|
|
103
|
+
// `control_files`: a family the policy hands a role that this sandbox
|
|
104
|
+
// protects whatever the settings say (tartaros's trust-handoff set holds
|
|
105
|
+
// `.vscode` at any depth), so the role could not do what it was given.
|
|
106
|
+
const families = role ? (config.roles?.[role]?.controlFiles ?? []) : [];
|
|
107
|
+
const kept = families.filter((f) => (sandbox.keepsControlFiles ?? []).includes(f));
|
|
108
|
+
const control = kept.length
|
|
109
|
+
? [`control_files (${kept.join(", ")}): ${sandbox.name} protects those files at any depth whatever the policy says, so the role could not edit them`]
|
|
110
|
+
: [];
|
|
111
|
+
return { sandbox, bin, unmet: [...(masking ? [masking] : []), ...unmet, ...browser, ...exemption, ...ruled, ...control], warnings, missing: bin ? null : sandbox.missing() };
|
|
95
112
|
});
|
|
96
113
|
const chosen = options.find((o) => o.bin && o.unmet.length === 0) ?? null;
|
|
97
114
|
return { chosen, options };
|
|
@@ -114,7 +131,7 @@ export function chooseSandbox(config, settings, { platform = process.platform, r
|
|
|
114
131
|
const { chosen, options } = weigh(config, settings, { platform, role, file });
|
|
115
132
|
if (chosen) return { sandbox: chosen.sandbox, bin: chosen.bin, options };
|
|
116
133
|
// The one sandbox there is, missing: said exactly as it always was.
|
|
117
|
-
if (options.length === 1 && options[0].missing
|
|
134
|
+
if (options.length === 1 && options[0].missing)
|
|
118
135
|
return { error: new Error(options[0].missing), options };
|
|
119
136
|
return { error: new Error(refusal(role, options)), options };
|
|
120
137
|
}
|
|
@@ -125,6 +142,6 @@ export function refusal(role, options) {
|
|
|
125
142
|
: ` - ${o.sandbox.name} cannot enforce all of it:\n${o.unmet.map((u) => ` ${u}`).join("\n")}`);
|
|
126
143
|
return `no sandbox in [sandbox] provider can hold ${role ? `role ${role}` : "this role"} as its policy is written, so it was not started:\n` +
|
|
127
144
|
`${lines.join("\n")}\n` +
|
|
128
|
-
` A sandbox that enforces less than the policy is never used in its place.
|
|
129
|
-
`or change the role.`;
|
|
145
|
+
` A sandbox that enforces less than the policy is never used in its place. Install one that can ` +
|
|
146
|
+
`(srt: npm install -g ${SRT_PACKAGE}@${SRT_VERSION}), name it in [sandbox] provider, or change the role.`;
|
|
130
147
|
}
|
package/src/sandboxes/srt.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `srt`: @anthropic-ai/sandbox-runtime, the sandbox seisin was built on.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The reference every other provider is measured against: the settings seisin
|
|
5
|
+
* generates (srt-settings.js, one level up) ARE this runtime's settings file, so
|
|
6
|
+
* it enforces all of them by construction and `unmet` is always empty. What a
|
|
7
|
+
* run hands it is the file and the command, nothing else.
|
|
8
|
+
*
|
|
9
|
+
* Not a dependency since 0.7: tartaros is the default and installs with seisin.
|
|
10
|
+
* srt is used where it is installed apart (globally, or in the project), after
|
|
11
|
+
* tartaros or where `[sandbox] provider` names it, and the version it is tested
|
|
12
|
+
* with is the one in seisin's devDependencies.
|
|
8
13
|
*/
|
|
9
14
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
10
15
|
import { join, dirname, delimiter } from "node:path";
|
|
@@ -47,6 +52,15 @@ export function resolveSrt({ from = HERE, path = process.env.PATH } = {}) {
|
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
const RUNTIME = "@anthropic-ai/sandbox-runtime";
|
|
55
|
+
export const SRT_PACKAGE = RUNTIME;
|
|
56
|
+
/** The srt seisin is tested with (its devDependency), for the install hint. */
|
|
57
|
+
export const SRT_VERSION = (() => {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(readFileSync(join(HERE, "..", "..", "package.json"), "utf8")).devDependencies?.[RUNTIME] ?? "latest";
|
|
60
|
+
} catch {
|
|
61
|
+
return "latest";
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
50
64
|
|
|
51
65
|
/**
|
|
52
66
|
* The `srt` of the sandbox-runtime that resolves from `from`, or null.
|
|
@@ -148,11 +162,17 @@ export function withMachPreload(bin, file, inner) {
|
|
|
148
162
|
export const srt = {
|
|
149
163
|
name: "srt",
|
|
150
164
|
locate: () => resolveSrt(),
|
|
151
|
-
missing: () => `sandbox
|
|
165
|
+
missing: () => `sandbox-runtime (srt) not found: it is not installed with seisin since 0.7. ` +
|
|
166
|
+
`Install it with: npm install -g ${RUNTIME}@${SRT_VERSION}`,
|
|
152
167
|
unmet: () => [],
|
|
153
|
-
// Masked credentials: on Linux
|
|
168
|
+
// Masked credentials: on Linux. On macOS srt masks too, but it reads the real
|
|
169
|
+
// value from its own environment, and from inside its sandbox the agent can
|
|
170
|
+
// read that environment back (measured with 0.0.81).
|
|
171
|
+
// anthropics/sandbox-runtime#593 takes the value out of there.
|
|
154
172
|
masks: (platform) => platform === "linux" ? null
|
|
155
|
-
:
|
|
173
|
+
: platform === "darwin"
|
|
174
|
+
? `masked credentials (key_mode = "inject") on darwin: sandbox-runtime masks them, but holds the real value in its own environment, which the agent can read from inside the sandbox (anthropics/sandbox-runtime#593 takes it out of there); tartaros does not keep it there`
|
|
175
|
+
: `masked credentials (key_mode = "inject") on ${platform}: sandbox-runtime does not mask them there; tartaros does`,
|
|
156
176
|
// `--` before the command, and it is not cosmetic. Without it the runtime's
|
|
157
177
|
// own argument parser reaches into what the agent was invoked with and eats
|
|
158
178
|
// anything resembling one of its flags: `claude --settings`, `claude -c`.
|
|
@@ -31,11 +31,10 @@ export const TARTAROS_PACKAGE = "@carlostapiacl/tartaros";
|
|
|
31
31
|
* the order srt is found in (resolveSrt in sandboxes/srt.js), for the same reason. A tartaros built
|
|
32
32
|
* once and left on PATH goes on answering after the pinned one has its fixes.
|
|
33
33
|
*
|
|
34
|
-
* The pinned one is
|
|
35
|
-
* binary
|
|
34
|
+
* The pinned one is a dependency: a main package whose binaryPath() finds the
|
|
35
|
+
* binary of this platform's package (an optional dependency of its own) whether
|
|
36
36
|
* npm nested it under the main package (a global install, measured) or hoisted
|
|
37
|
-
* it.
|
|
38
|
-
* binary) falls through to PATH.
|
|
37
|
+
* it. A platform without a binary, or `--omit=optional`, falls through to PATH.
|
|
39
38
|
*/
|
|
40
39
|
export function resolveTartaros({ from = HERE, path = process.env.PATH } = {}) {
|
|
41
40
|
const pinned = dependencyTartaros(from);
|
|
@@ -116,7 +115,10 @@ export function tartarosSettings(settings, { machRegister = [], handoffExempt =
|
|
|
116
115
|
export const tartaros = {
|
|
117
116
|
name: "tartaros",
|
|
118
117
|
locate: () => resolveTartaros(),
|
|
119
|
-
missing: () => `tartaros not found: neither ${TARTAROS_PACKAGE}, which installs with seisin
|
|
118
|
+
missing: () => `tartaros not found: neither ${TARTAROS_PACKAGE}, which installs with seisin (it has no binary for ${process.platform}-${process.arch}), nor tartaros on PATH.`,
|
|
119
|
+
// Its trust-handoff set holds the editors' folders (`.vscode`, `.idea`) at any
|
|
120
|
+
// depth, whatever the settings say: a role given them cannot edit them here.
|
|
121
|
+
keepsControlFiles: ["ide"],
|
|
120
122
|
asksFile: true,
|
|
121
123
|
// Masked credentials: tartaros says whether it masks them, through explain
|
|
122
124
|
// (0.1.0-alpha.16 and later do; earlier ones refuse the field).
|