vigiles 12.2.0 → 12.4.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.
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ /**
3
+ * vigiles — R3 disposable-service tier (⚠️ EXPERIMENTAL / UNSTABLE).
4
+ *
5
+ * ─────────────────────────────────────────────────────────────────────────────
6
+ * EXPERIMENTAL: this surface is a DRAFT. Import it from `vigiles/experimental`,
7
+ * NOT from a stable subpath. It is NOT covered by the stability guarantee and
8
+ * may change shape or be removed WITHOUT a major-version bump. Do not build a
9
+ * production workflow on it yet. See docs/measuring-skills.md § Experimental.
10
+ * ─────────────────────────────────────────────────────────────────────────────
11
+ *
12
+ * WHAT THIS IS. The free tiers (`runHook`, `measureTriggerRate`, `runEval`)
13
+ * cover a skill's OUTPUT and its side-effect SAFETY (did it call / not call a
14
+ * tool, write / not write a file) with no container. What they do NOT do is let
15
+ * a skill actually PERFORM a side effect against a real service and verify the
16
+ * resulting state — apply a migration to a real Postgres and check the row
17
+ * landed. That is the R3 rung (see research/eval-coverage-and-isolation.md): the
18
+ * REAL system's semantics are the thing under test, so it can't be faked.
19
+ *
20
+ * THE POSTURE. vigiles COMPOSES with a throwaway container; it does not reinvent
21
+ * the sandbox. A {@link ServiceSpec} declares a disposable service; the injected
22
+ * {@link ContainerRuntime} port starts it, and it is force-removed on teardown.
23
+ *
24
+ * ⚠️ SAFETY — the isolation is the DISPOSABLE CONTAINER, nothing more. A skill is
25
+ * model-driven, so the MODEL chooses the actions: it can do anything the run
26
+ * ENVIRONMENT allows. vigiles creates and destroys the container; it does NOT, in
27
+ * this tier, confine the skill's filesystem or block its network. So an R3 run is
28
+ * only as safe as the environment you run it in:
29
+ * - run it in a DISPOSABLE environment — a CI job, a throwaway container/VM, or
30
+ * a dev box with NO production access;
31
+ * - point the task at the disposable service's connection string ONLY;
32
+ * - keep real credentials OUT of the run (prod `DATABASE_URL`, cloud keys,
33
+ * `~/.ssh`) — pair it with the eval tier's `ephemeralEnv` (throwaway HOME +
34
+ * cleared env) to scrub them so the model has no real keys to misuse.
35
+ * Treat it like running an untrusted script. A future increment adds an egress
36
+ * wall (the skill reaches only the model + the service); until then that job is
37
+ * the operator's. See docs/measuring-skills.md § Experimental.
38
+ *
39
+ * WHAT SHIPS TODAY vs LATER. Today: the TYPES, the {@link ContainerRuntime} port,
40
+ * the pure {@link experimental_startServices} / {@link experimental_withServices}
41
+ * orchestration, and a Docker backend (`src/services-docker.ts`). Deferred: the
42
+ * `runEval` / `measureArms` `services` option + `ctx.service(name)`, per-trial
43
+ * reset via an eval-loop hook, and the egress wall. Requires Docker (Linux-first).
44
+ * It is an explicit opt-in and NEVER part of `vigiles audit` (audit stays
45
+ * side-effect-free).
46
+ *
47
+ * @experimental
48
+ * @module vigiles/experimental (services)
49
+ */
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.experimental_startServices = experimental_startServices;
52
+ exports.experimental_withServices = experimental_withServices;
53
+ /**
54
+ * Start every declared service against an injected {@link ContainerRuntime},
55
+ * returning a {@link ServiceSession}. Pure orchestration over the port — no
56
+ * `docker` is imported here, so this is fully unit-testable with a fake runtime
57
+ * (the `decideSandbox`-is-pure / `runSandboxed`-is-real split). If any service
58
+ * fails to start, the ones already up are torn down before the error propagates,
59
+ * so a partial failure never leaks a container.
60
+ *
61
+ * A Docker-backed `ContainerRuntime` and the `runEval` / `measureArms` wiring are
62
+ * the next increment — until then this is the primitive you compose by hand:
63
+ * start → pin egress to `session.endpoints` → run → read `session.handles` in
64
+ * `measure` → `await session.teardown()`.
65
+ *
66
+ * @experimental — surface may change without a major-version bump.
67
+ */
68
+ async function experimental_startServices(services, runtime) {
69
+ const handles = {};
70
+ const endpoints = [];
71
+ const started = [];
72
+ const stopAll = async () => {
73
+ await Promise.allSettled(started.map((h) => runtime.stop(h)));
74
+ };
75
+ try {
76
+ for (const [name, spec] of Object.entries(services)) {
77
+ const handle = await runtime.start(name, spec);
78
+ handles[name] = handle;
79
+ started.push(handle);
80
+ // Only a service that published a port has a reachable endpoint.
81
+ if (handle.port !== undefined) {
82
+ endpoints.push(`${handle.host}:${handle.port}`);
83
+ }
84
+ }
85
+ }
86
+ catch (err) {
87
+ await stopAll();
88
+ throw err;
89
+ }
90
+ return { handles, endpoints, teardown: stopAll };
91
+ }
92
+ /**
93
+ * Run `fn` with the declared services up, disposing them afterwards — even if
94
+ * `fn` throws. The scope-guard form of {@link experimental_startServices}: it
95
+ * removes the manual `try/finally` so a `measureArms` / `measure` call can be
96
+ * wrapped in one line and its containers are always cleaned up.
97
+ *
98
+ * ⚠️ Read the SAFETY note in this module's header first — the container is the
99
+ * only isolation; keep real credentials out of the run.
100
+ *
101
+ * LIFECYCLE NOTE (honest): the services live for the WHOLE `fn` — i.e. per-RUN,
102
+ * not per-trial. An eval that mutates service state across trials should make its
103
+ * task self-contained (e.g. `drop … if exists; create; migrate`) or run
104
+ * `trials: 1`. True per-trial reset needs an eval-loop hook and is the next
105
+ * increment (research/r3-disposable-services.md).
106
+ *
107
+ * @experimental — surface may change without a major-version bump.
108
+ */
109
+ async function experimental_withServices(services, runtime, fn) {
110
+ const session = await experimental_startServices(services, runtime);
111
+ try {
112
+ return await fn(session);
113
+ }
114
+ finally {
115
+ await session.teardown();
116
+ }
117
+ }
118
+ //# sourceMappingURL=services.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigiles",
3
- "version": "12.2.0",
3
+ "version": "12.4.0",
4
4
  "description": "Lint & test the harness your AI agent runs on — verify the references in your CLAUDE.md / AGENTS.md and test that your hooks and skills actually work.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -44,6 +44,7 @@
44
44
  "./claude-code": "./dist/claude-code.js",
45
45
  "./codex": "./dist/codex.js",
46
46
  "./adapter": "./dist/adapter.js",
47
+ "./experimental": "./dist/experimental.js",
47
48
  "./vitest": {
48
49
  "types": "./dist/vitest.d.mts",
49
50
  "default": "./dist/vitest.mjs"
@@ -79,7 +80,7 @@
79
80
  "test:e2e": "npm run build && vitest run --project e2e",
80
81
  "test:cli-e2e": "bash test/e2e/run.sh",
81
82
  "test:harness": "npm run build && node dist/cli.js test",
82
- "test:eval": "npm run build && node dist/cli.js eval",
83
+ "test:eval": "npm run build && node dist/cli.js eval --all",
83
84
  "test:vitest": "npm run build && vitest run --project runners",
84
85
  "test:jest": "npm run build && jest",
85
86
  "test:types": "npm run build && tsc --noEmit -p test/types/tsconfig.json",