agy-worker-mcp 0.3.1
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/CHANGELOG.md +141 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/commands/agy-ceiling.md +31 -0
- package/dist/broker/blockers.d.ts +86 -0
- package/dist/broker/blockers.d.ts.map +1 -0
- package/dist/broker/blockers.js +390 -0
- package/dist/broker/blockers.js.map +1 -0
- package/dist/broker/outcome.d.ts +85 -0
- package/dist/broker/outcome.d.ts.map +1 -0
- package/dist/broker/outcome.js +147 -0
- package/dist/broker/outcome.js.map +1 -0
- package/dist/broker/reconcile.d.ts +74 -0
- package/dist/broker/reconcile.d.ts.map +1 -0
- package/dist/broker/reconcile.js +415 -0
- package/dist/broker/reconcile.js.map +1 -0
- package/dist/broker/result.d.ts +88 -0
- package/dist/broker/result.d.ts.map +1 -0
- package/dist/broker/result.js +234 -0
- package/dist/broker/result.js.map +1 -0
- package/dist/broker/verify.d.ts +52 -0
- package/dist/broker/verify.d.ts.map +1 -0
- package/dist/broker/verify.js +174 -0
- package/dist/broker/verify.js.map +1 -0
- package/dist/contract/errors.d.ts +119 -0
- package/dist/contract/errors.d.ts.map +1 -0
- package/dist/contract/errors.js +99 -0
- package/dist/contract/errors.js.map +1 -0
- package/dist/contract/index.d.ts +10 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +10 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/paths.d.ts +139 -0
- package/dist/contract/paths.d.ts.map +1 -0
- package/dist/contract/paths.js +359 -0
- package/dist/contract/paths.js.map +1 -0
- package/dist/contract/schema.sql +64 -0
- package/dist/contract/types.d.ts +1113 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +114 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/events/cursor.d.ts +65 -0
- package/dist/events/cursor.d.ts.map +1 -0
- package/dist/events/cursor.js +286 -0
- package/dist/events/cursor.js.map +1 -0
- package/dist/events/detect.d.ts +58 -0
- package/dist/events/detect.d.ts.map +1 -0
- package/dist/events/detect.js +183 -0
- package/dist/events/detect.js.map +1 -0
- package/dist/events/normalize.d.ts +30 -0
- package/dist/events/normalize.d.ts.map +1 -0
- package/dist/events/normalize.js +158 -0
- package/dist/events/normalize.js.map +1 -0
- package/dist/events/parse.d.ts +41 -0
- package/dist/events/parse.d.ts.map +1 -0
- package/dist/events/parse.js +89 -0
- package/dist/events/parse.js.map +1 -0
- package/dist/gate/bind.d.ts +28 -0
- package/dist/gate/bind.d.ts.map +1 -0
- package/dist/gate/bind.js +154 -0
- package/dist/gate/bind.js.map +1 -0
- package/dist/gate/gate.d.ts +139 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +488 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/gate/hooks-file.d.ts +55 -0
- package/dist/gate/hooks-file.d.ts.map +1 -0
- package/dist/gate/hooks-file.js +104 -0
- package/dist/gate/hooks-file.js.map +1 -0
- package/dist/gate.d.ts +3 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +20 -0
- package/dist/gate.js.map +1 -0
- package/dist/policy/ceiling-review.d.ts +22 -0
- package/dist/policy/ceiling-review.d.ts.map +1 -0
- package/dist/policy/ceiling-review.js +170 -0
- package/dist/policy/ceiling-review.js.map +1 -0
- package/dist/policy/ceiling.d.ts +128 -0
- package/dist/policy/ceiling.d.ts.map +1 -0
- package/dist/policy/ceiling.js +273 -0
- package/dist/policy/ceiling.js.map +1 -0
- package/dist/policy/containment.d.ts +100 -0
- package/dist/policy/containment.d.ts.map +1 -0
- package/dist/policy/containment.js +1071 -0
- package/dist/policy/containment.js.map +1 -0
- package/dist/policy/hard-deny.d.ts +28 -0
- package/dist/policy/hard-deny.d.ts.map +1 -0
- package/dist/policy/hard-deny.js +78 -0
- package/dist/policy/hard-deny.js.map +1 -0
- package/dist/policy/profiles.d.ts +82 -0
- package/dist/policy/profiles.d.ts.map +1 -0
- package/dist/policy/profiles.js +317 -0
- package/dist/policy/profiles.js.map +1 -0
- package/dist/policy/rules.d.ts +138 -0
- package/dist/policy/rules.d.ts.map +1 -0
- package/dist/policy/rules.js +1180 -0
- package/dist/policy/rules.js.map +1 -0
- package/dist/policy/seatbelt.d.ts +32 -0
- package/dist/policy/seatbelt.d.ts.map +1 -0
- package/dist/policy/seatbelt.js +59 -0
- package/dist/policy/seatbelt.js.map +1 -0
- package/dist/policy/tools.d.ts +36 -0
- package/dist/policy/tools.d.ts.map +1 -0
- package/dist/policy/tools.js +94 -0
- package/dist/policy/tools.js.map +1 -0
- package/dist/runner/gate-watchdog.d.ts +93 -0
- package/dist/runner/gate-watchdog.d.ts.map +1 -0
- package/dist/runner/gate-watchdog.js +153 -0
- package/dist/runner/gate-watchdog.js.map +1 -0
- package/dist/runner/idle.d.ts +51 -0
- package/dist/runner/idle.d.ts.map +1 -0
- package/dist/runner/idle.js +81 -0
- package/dist/runner/idle.js.map +1 -0
- package/dist/runner/inbox.d.ts +56 -0
- package/dist/runner/inbox.d.ts.map +1 -0
- package/dist/runner/inbox.js +134 -0
- package/dist/runner/inbox.js.map +1 -0
- package/dist/runner/reap.d.ts +65 -0
- package/dist/runner/reap.d.ts.map +1 -0
- package/dist/runner/reap.js +168 -0
- package/dist/runner/reap.js.map +1 -0
- package/dist/runner/runner.d.ts +51 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +289 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/runner/spawn.d.ts +98 -0
- package/dist/runner/spawn.d.ts.map +1 -0
- package/dist/runner/spawn.js +230 -0
- package/dist/runner/spawn.js.map +1 -0
- package/dist/runner/verify.d.ts +24 -0
- package/dist/runner/verify.d.ts.map +1 -0
- package/dist/runner/verify.js +108 -0
- package/dist/runner/verify.js.map +1 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +15 -0
- package/dist/runner.js.map +1 -0
- package/dist/server/context.d.ts +41 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +59 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/instructions.d.ts +13 -0
- package/dist/server/instructions.d.ts.map +1 -0
- package/dist/server/instructions.js +121 -0
- package/dist/server/instructions.js.map +1 -0
- package/dist/server/server.d.ts +20 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +55 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/tools/cancel.d.ts +24 -0
- package/dist/server/tools/cancel.d.ts.map +1 -0
- package/dist/server/tools/cancel.js +59 -0
- package/dist/server/tools/cancel.js.map +1 -0
- package/dist/server/tools/capabilities.d.ts +36 -0
- package/dist/server/tools/capabilities.d.ts.map +1 -0
- package/dist/server/tools/capabilities.js +116 -0
- package/dist/server/tools/capabilities.js.map +1 -0
- package/dist/server/tools/ceiling.d.ts +31 -0
- package/dist/server/tools/ceiling.d.ts.map +1 -0
- package/dist/server/tools/ceiling.js +121 -0
- package/dist/server/tools/ceiling.js.map +1 -0
- package/dist/server/tools/index.d.ts +38 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/index.js +115 -0
- package/dist/server/tools/index.js.map +1 -0
- package/dist/server/tools/listJobs.d.ts +30 -0
- package/dist/server/tools/listJobs.d.ts.map +1 -0
- package/dist/server/tools/listJobs.js +37 -0
- package/dist/server/tools/listJobs.js.map +1 -0
- package/dist/server/tools/logs.d.ts +31 -0
- package/dist/server/tools/logs.d.ts.map +1 -0
- package/dist/server/tools/logs.js +96 -0
- package/dist/server/tools/logs.js.map +1 -0
- package/dist/server/tools/result.d.ts +32 -0
- package/dist/server/tools/result.d.ts.map +1 -0
- package/dist/server/tools/result.js +95 -0
- package/dist/server/tools/result.js.map +1 -0
- package/dist/server/tools/send.d.ts +37 -0
- package/dist/server/tools/send.d.ts.map +1 -0
- package/dist/server/tools/send.js +71 -0
- package/dist/server/tools/send.js.map +1 -0
- package/dist/server/tools/sessions.d.ts +28 -0
- package/dist/server/tools/sessions.d.ts.map +1 -0
- package/dist/server/tools/sessions.js +54 -0
- package/dist/server/tools/sessions.js.map +1 -0
- package/dist/server/tools/start.d.ts +107 -0
- package/dist/server/tools/start.d.ts.map +1 -0
- package/dist/server/tools/start.js +482 -0
- package/dist/server/tools/start.js.map +1 -0
- package/dist/server/tools/wait.d.ts +29 -0
- package/dist/server/tools/wait.d.ts.map +1 -0
- package/dist/server/tools/wait.js +120 -0
- package/dist/server/tools/wait.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +17 -0
- package/dist/server.js.map +1 -0
- package/dist/setup/install.d.ts +52 -0
- package/dist/setup/install.d.ts.map +1 -0
- package/dist/setup/install.js +108 -0
- package/dist/setup/install.js.map +1 -0
- package/dist/setup.d.ts +3 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +24 -0
- package/dist/setup.js.map +1 -0
- package/dist/store/db.d.ts +49 -0
- package/dist/store/db.d.ts.map +1 -0
- package/dist/store/db.js +101 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/jobs.d.ts +45 -0
- package/dist/store/jobs.d.ts.map +1 -0
- package/dist/store/jobs.js +150 -0
- package/dist/store/jobs.js.map +1 -0
- package/dist/store/locks.d.ts +60 -0
- package/dist/store/locks.d.ts.map +1 -0
- package/dist/store/locks.js +172 -0
- package/dist/store/locks.js.map +1 -0
- package/dist/store/sessions.d.ts +37 -0
- package/dist/store/sessions.d.ts.map +1 -0
- package/dist/store/sessions.js +117 -0
- package/dist/store/sessions.js.map +1 -0
- package/dist/trace/digest.d.ts +77 -0
- package/dist/trace/digest.d.ts.map +1 -0
- package/dist/trace/digest.js +481 -0
- package/dist/trace/digest.js.map +1 -0
- package/package.json +68 -0
- package/skills/agy-ceiling/SKILL.md +72 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to agy-worker-mcp. Dates are the day the version landed on
|
|
4
|
+
`main`. Measurements against the real `agy` CLI are noted with the agy version
|
|
5
|
+
they were taken on.
|
|
6
|
+
|
|
7
|
+
## 0.3.1 — 2026-09-09
|
|
8
|
+
|
|
9
|
+
Pre-publish audit. The gate is a string classifier; this release makes it
|
|
10
|
+
reject what it does not model instead of passing it, and says so in the docs.
|
|
11
|
+
|
|
12
|
+
- **Chain and wrapper parsing** — newline, `\r\n`, single `&` and `|&` split
|
|
13
|
+
segments; `<( )` / `>( )` are judged like `$( )`; `bash -lc`-style clusters
|
|
14
|
+
count as `-c`; only `bash`, `sh`, `env` act as wrappers (`zsh -c` needs its own
|
|
15
|
+
rule); a head token carrying `\`, `$`, quotes or non-ASCII is a parse error.
|
|
16
|
+
- **`rm`, `git`, `find`, `xargs`** — `rm` denies on any `-r`/`-R` + `-f`
|
|
17
|
+
combination; `git -c` / `-C` / `--git-dir` / `--work-tree` / `--exec-path` /
|
|
18
|
+
`--config-env` and `git config` writes to executable keys are always denied;
|
|
19
|
+
`find -exec` / `-execdir` / `-ok` / `-delete` are denied; `xargs` left the
|
|
20
|
+
`general_worker` allow list and `xargs <cmd>` is judged as `<cmd>`.
|
|
21
|
+
- **Containment** — every path is resolved against the pinned workspace, never
|
|
22
|
+
the model's `Cwd`; a leading `~/` expands to the real home, any other `~`,
|
|
23
|
+
`$VAR` or backtick in a path is refused (`unexpanded_path`); globs are
|
|
24
|
+
contained by their literal directory prefix; `sed -i`, `sort -o`, `tee`,
|
|
25
|
+
`dd of=`, `tar -C`, `unzip -d`, `git clone/archive/init/worktree`, … count
|
|
26
|
+
as writers; read utilities and script paths (`cat`, `grep`, `node x.js`, …)
|
|
27
|
+
must stay inside the workspace or `read_roots` (`read_outside_workspace`).
|
|
28
|
+
- **Credential `HARD_DENY` for commands** — `~/.ssh`, `~/.aws`, `~/.gnupg`,
|
|
29
|
+
`~/.netrc`, `~/.npmrc`, `~/.git-credentials`, `~/.config/gh`,
|
|
30
|
+
`~/.config/gcloud`, `~/.docker/config.json`, `~/.kube`, `~/.gemini`,
|
|
31
|
+
`~/.antigravity`, `~/.agy-worker` are refused as command arguments and
|
|
32
|
+
redirection targets, not only through file tools; the file-tool list gained
|
|
33
|
+
the same entries; the home directory is canonicalised before matching.
|
|
34
|
+
- **Self-protection** — `agy_start` refuses a workspace that contains the state
|
|
35
|
+
home or the gate binary, and a ceiling `write_roots` entry of `/`, the home
|
|
36
|
+
directory or the state home.
|
|
37
|
+
- **Hygiene** — job output files `0600`, job and project directories `0700`,
|
|
38
|
+
`index.db` and its WAL/SHM `0600`.
|
|
39
|
+
- **Docs** — README carries an unofficial/trademark notice and a
|
|
40
|
+
"Trademarks and terms" section on how the official `agy` binary is used;
|
|
41
|
+
`docs/permissions.md` gains "What the gate deliberately does not see"
|
|
42
|
+
(threat model, what allowed programs can do, sandbox default, local trust,
|
|
43
|
+
where the string match ends).
|
|
44
|
+
|
|
45
|
+
## 0.3.0 — 2026-09-09
|
|
46
|
+
|
|
47
|
+
Two walls, kept apart: the profile (code) and the project ceiling (a human).
|
|
48
|
+
|
|
49
|
+
- **Ceiling file version 2** — `allow` / `deny` / `exceptions` / `read_roots` /
|
|
50
|
+
`write_roots` / `sandbox` / `command_policy`. A version 1 file
|
|
51
|
+
(`extra_allow`, `extra_deny`, `additional_dirs`, `sandboxed`) is still read,
|
|
52
|
+
converted, and warned about; it stops loading in 0.4.
|
|
53
|
+
- **`HARD_DENY` narrowed** to what protects the gate itself (credential reads,
|
|
54
|
+
writes into `{workspace}/.agents`). Everything else `general_worker` denies —
|
|
55
|
+
`git push`, `curl`, `wget`, `ssh`, `scp`, `sudo`, `docker`, `rm -rf`,
|
|
56
|
+
`git reset --hard`, `npm install`, `pip install`, … — can be lifted by a
|
|
57
|
+
project through `exceptions`. By a human editing the file, never by a request.
|
|
58
|
+
- **`sandbox: "seatbelt"`** — our own write-only `sandbox-exec` profile (macOS):
|
|
59
|
+
an allowed command runs with writes confined to the workspace and the
|
|
60
|
+
ceiling's `write_roots`, even through interpreters, and still does the work.
|
|
61
|
+
`"agy"` keeps agy's own sandbox; `"none"` stays the default. Strictest of
|
|
62
|
+
profile / ceiling / request wins; the request's `sandbox` replaces the old
|
|
63
|
+
boolean `sandboxed` (still accepted, means `"agy"`).
|
|
64
|
+
- **`agy_ceiling`** — a tenth tool. Read-only: the ceiling file as loaded, the
|
|
65
|
+
effective policy, the project's denial history, a review of a draft (risk
|
|
66
|
+
class per rule, `HARD_DENY` conflicts, duplicates of the profile) and how
|
|
67
|
+
`expected_commands` would be judged. It has no write path.
|
|
68
|
+
- **`agy-ceiling` skill and `/agy-ceiling` slash command** (Claude Code),
|
|
69
|
+
shipped in the package under `skills/` and `commands/`; both stop for the
|
|
70
|
+
user's approval before the file is written. `agy-worker-setup` copies them
|
|
71
|
+
into `./.claude` or `~/.claude`.
|
|
72
|
+
- **No-ceiling hint** — when a project has no ceiling file, `agy_capabilities`,
|
|
73
|
+
`agy_start` and a blocked job's `verification.warnings` say so once, name
|
|
74
|
+
the file, and point at the three ways to propose one.
|
|
75
|
+
- **`agy_logs({ stream: "digest" })`** — what a job actually ran and touched:
|
|
76
|
+
commands with allow/deny, files written, denials, in order.
|
|
77
|
+
- **Denial policy** — `max_denials` aborts after N gate denials regardless of
|
|
78
|
+
`on_denial`; an `unsupported` tool call no longer aborts a job on its own
|
|
79
|
+
(subagent tools still do); `schedule` is a control tool; the environment-block
|
|
80
|
+
detector reads only the last five lines of a tool's output, so a `git diff`
|
|
81
|
+
that quotes an error message is no longer a "sandbox block".
|
|
82
|
+
- **Read roots that carry their own `.agents/hooks.json` are refused** at
|
|
83
|
+
`agy_start` — agy loads hooks from every `--add-dir`, and a foreign deny runs
|
|
84
|
+
ahead of our gate (measured, agy 1.1.27).
|
|
85
|
+
- Release workflow (`.github/workflows/release.yml`): tag `v*.*.*` matching
|
|
86
|
+
`package.json`, or manual dispatch with `dry_run` (default true).
|
|
87
|
+
|
|
88
|
+
## 0.2.2 — 2026-09-08
|
|
89
|
+
|
|
90
|
+
From a usage audit of 35 real jobs.
|
|
91
|
+
|
|
92
|
+
- The broker no longer misjudges a job as `process_error` while its
|
|
93
|
+
`verify_command` is still running; `duration_ms` is the runner's real end.
|
|
94
|
+
- The gate parses `ENV=… cmd`, `env`, `export`, `&&` / `|` / `;` chains and
|
|
95
|
+
`bash -c` instead of denying them wholesale.
|
|
96
|
+
- The ceiling's read roots apply to every job by default; a request names
|
|
97
|
+
them only to pick a subset.
|
|
98
|
+
- `general_worker` allows the ordinary POSIX utilities and inline
|
|
99
|
+
interpreters, with `git` under a denylist; containment still bounds `rm`,
|
|
100
|
+
`mv`, `cp`, `touch`, `mkdir` to the workspace.
|
|
101
|
+
- A ceiling can opt into `command_policy: "denylist"`.
|
|
102
|
+
- `agy_start` warns up front when a job has nothing checkable, judges
|
|
103
|
+
`expected_commands` on a `dry_run`, and checks `model` / `effort` against
|
|
104
|
+
what agy 1.1.27 accepts.
|
|
105
|
+
- `agy_wait` on a running job shows the tail of the log, not its head.
|
|
106
|
+
|
|
107
|
+
## 0.2.1 — 2026-09-03
|
|
108
|
+
|
|
109
|
+
- Allowed commands on `general_worker` run **without** agy's OS sandbox.
|
|
110
|
+
Measured on agy 1.1.24, that sandbox refuses every write a shell command
|
|
111
|
+
makes inside the workspace — `npm test`, `./gradlew build`, `git commit` all
|
|
112
|
+
fail with `Operation not permitted` after the gate said yes. The gate's
|
|
113
|
+
allow list is the boundary, as in Claude Code; `research_readonly` stays
|
|
114
|
+
sandboxed; a ceiling can force the sandbox back on.
|
|
115
|
+
- The `unsandboxed` ceiling key was removed (a file carrying it fails closed
|
|
116
|
+
with a migration message).
|
|
117
|
+
|
|
118
|
+
## 0.2.0 — 2026-09-02
|
|
119
|
+
|
|
120
|
+
- Permission enforcement rebuilt around a single `PreToolUse` gate that is the
|
|
121
|
+
sole approval authority for every job; agy's own approval engine is disabled
|
|
122
|
+
(`--dangerously-skip-permissions`). A bound job's gate never answers "ask".
|
|
123
|
+
- Human-owned per-project ceiling file, `~/.agy-worker/projects/<hash>/policy.json`,
|
|
124
|
+
outside the workspace.
|
|
125
|
+
- `verify_command`: a command the runner runs once after agy exits, against
|
|
126
|
+
the final tree; the job cannot skip or narrow it.
|
|
127
|
+
- The `network` permission field was dropped.
|
|
128
|
+
- Subagent tools are always denied; the runner kills a job whose gate never
|
|
129
|
+
confirmed itself.
|
|
130
|
+
|
|
131
|
+
## 0.1.1 — 2026-09-02
|
|
132
|
+
|
|
133
|
+
- Build commands (`./gradlew`, `mvn`, `npm test`, `npm run`, `javac`, `java`)
|
|
134
|
+
added to the `general_worker` ceiling so they stop bouncing.
|
|
135
|
+
|
|
136
|
+
## 0.1.0 — 2026-09-01
|
|
137
|
+
|
|
138
|
+
- MVP: detached `agy` jobs with state in a project-local SQLite database,
|
|
139
|
+
`agy_start` / `agy_wait` / `agy_result` / `agy_logs` / `agy_send` /
|
|
140
|
+
`agy_cancel` / `agy_list_jobs` / `agy_sessions` / `agy_capabilities`, and a
|
|
141
|
+
broker-computed `outcome` kept separate from agy's self-report.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Siwoo Ryu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
> [!WARNING]
|
|
2
|
+
> Under active development (0.3.x). The permission model is a string classifier over
|
|
3
|
+
> tool calls — the same kind of boundary Claude Code and Codex use, not a kernel
|
|
4
|
+
> boundary. Allowed shell commands run **without** an OS sandbox by default since 0.2.1
|
|
5
|
+
> (can be enabled with `sandbox: "seatbelt"`). What the gate deliberately does not
|
|
6
|
+
> see is documented in [`docs/permissions.md`](./docs/permissions.md#what-the-gate-deliberately-does-not-see);
|
|
7
|
+
> read it before starting. Point it only at projects you would let Claude Code work on unattended.
|
|
8
|
+
|
|
9
|
+
# agy-worker-mcp
|
|
10
|
+
|
|
11
|
+
An MCP server that runs the Google Antigravity CLI (`agy`) as an asynchronous
|
|
12
|
+
worker agent, callable from Claude Code, Codex, and any other MCP client.
|
|
13
|
+
|
|
14
|
+
> [!NOTE]
|
|
15
|
+
> **Unofficial.** agy-worker-mcp is an independent, third-party MCP server. It is not affiliated with, endorsed by, or supported by Google. "Google", "Antigravity", "Gemini", and the `agy` command name are trademarks or product names of Google LLC and are used here only to identify the CLI this server drives. See [Trademarks and terms](#trademarks-and-terms).
|
|
16
|
+
|
|
17
|
+
Jobs are detached from the client that started them: a job's process outlives
|
|
18
|
+
the MCP connection, its stdout/stderr are redirected to files rather than
|
|
19
|
+
piped, and its state lives in a project-local SQLite database. Any client
|
|
20
|
+
connected to the same project can start a job, watch it, take it over after its
|
|
21
|
+
original caller disconnected, or resume its conversation later.
|
|
22
|
+
|
|
23
|
+
How it keeps a job honest: every tool call `agy` makes passes through our own
|
|
24
|
+
`PreToolUse` gate, which is the sole approval authority (`agy`'s own engine is
|
|
25
|
+
off). What a job may do is set by two walls — the shipped profile, and a
|
|
26
|
+
human-owned per-project ceiling file that lives outside the workspace — and a
|
|
27
|
+
client request can only narrow within them. Allowed shell commands run without
|
|
28
|
+
an OS sandbox by default, as under Claude Code; a project can put a write-only
|
|
29
|
+
kernel boundary back with `sandbox: "seatbelt"`. `agy_ceiling`, the shipped
|
|
30
|
+
`agy-ceiling` skill and the `/agy-ceiling` slash command let an agent or a user
|
|
31
|
+
propose that ceiling from the project's denial history — and nothing writes
|
|
32
|
+
the file without your approval. Version-by-version detail is in
|
|
33
|
+
[`CHANGELOG.md`](./CHANGELOG.md).
|
|
34
|
+
|
|
35
|
+
## Why detached jobs
|
|
36
|
+
|
|
37
|
+
`agy` runs turns that take minutes to an hour. A plain stdio MCP server tied to
|
|
38
|
+
one client's process would lose the job the moment that client disconnects, and
|
|
39
|
+
would give a second client (Codex checking on a job Claude Code started) no way
|
|
40
|
+
to see it. Detaching the process, redirecting its output to disk, and
|
|
41
|
+
coordinating through SQLite makes the job durable and visible independent of
|
|
42
|
+
who is currently connected.
|
|
43
|
+
|
|
44
|
+
## The one fact that matters most
|
|
45
|
+
|
|
46
|
+
**`agy`'s own exit code and status cannot be trusted.** A permission denial —
|
|
47
|
+
and, on a job that runs sandboxed, an OS-sandbox block — surfaces as `exit 0`
|
|
48
|
+
/ `status: SUCCESS`. `agy` itself does not know it was blocked, and will often
|
|
49
|
+
report success after quietly failing or working around the block.
|
|
50
|
+
|
|
51
|
+
Every result this server returns carries a broker-computed `outcome`, derived
|
|
52
|
+
from actual events, exit status, and filesystem checks, kept deliberately
|
|
53
|
+
separate from `agy`'s self-report (`agent_report`). Read `outcome` and
|
|
54
|
+
`contract_status`; never `agent_report.status`.
|
|
55
|
+
|
|
56
|
+
## Requirements
|
|
57
|
+
|
|
58
|
+
- Node.js ≥ 22.5
|
|
59
|
+
- The `agy` CLI on `PATH` (developed and measured against agy 1.1.24–1.1.27).
|
|
60
|
+
`agy_capabilities` tells you whether the server can find it.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install -g agy-worker-mcp
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
That puts `agy-worker-mcp`, `agy-worker-setup` and the two helper binaries
|
|
69
|
+
(`agy-worker-runner`, `agy-worker-gate`) on your `PATH`. No global install:
|
|
70
|
+
`npx -y agy-worker-mcp@latest` runs the server and
|
|
71
|
+
`npx -y -p agy-worker-mcp agy-worker-setup` runs the setup below.
|
|
72
|
+
|
|
73
|
+
Register it — Claude Code, project-scoped, which is easy to undo and affects
|
|
74
|
+
nothing else:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
claude mcp add agy --scope project -- agy-worker-mcp
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then, in the project where you use it, drop in the Claude Code pieces (the
|
|
81
|
+
`agy-ceiling` skill and the `/agy-ceiling` slash command — npm cannot put
|
|
82
|
+
files into `.claude/` for you, and this package refuses to write there
|
|
83
|
+
unasked):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
agy-worker-setup # → ./.claude/skills, ./.claude/commands
|
|
87
|
+
agy-worker-setup --scope user # → ~/.claude/…, once for every project
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
It never overwrites an existing file unless you pass `--force`, and `--dry-run`
|
|
91
|
+
shows the plan.
|
|
92
|
+
|
|
93
|
+
Codex (`~/.codex/config.toml`):
|
|
94
|
+
|
|
95
|
+
```toml
|
|
96
|
+
[mcp_servers.agy]
|
|
97
|
+
command = "agy-worker-mcp"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
<details>
|
|
101
|
+
<summary>From GitHub or a clone instead</summary>
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install -g github:thezoot3/agy-worker-mcp # builds on install (prepare)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
git clone https://github.com/thezoot3/agy-worker-mcp.git
|
|
109
|
+
cd agy-worker-mcp
|
|
110
|
+
npm install # `prepare` builds dist/ for you
|
|
111
|
+
claude mcp add agy --scope project -- node "$PWD/dist/server.js"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Registering by absolute path means the server runs whatever is in `dist/` —
|
|
115
|
+
re-run `npm run build` after editing `src/`.
|
|
116
|
+
</details>
|
|
117
|
+
|
|
118
|
+
Check the registration with `claude mcp list`, and remove it with
|
|
119
|
+
`claude mcp remove agy --scope project`.
|
|
120
|
+
|
|
121
|
+
The server discovers the project root by walking up from its `cwd` to a git
|
|
122
|
+
root, or honors `AGY_WORKER_PROJECT` as an override. Per-project state lives
|
|
123
|
+
under `~/.agy-worker/projects/<hash>/` — never inside your repository, so
|
|
124
|
+
nothing here needs a `.gitignore` entry.
|
|
125
|
+
|
|
126
|
+
## Quick start
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
agy_capabilities -- profiles, models, discovered root
|
|
130
|
+
agy_start { prompt, profile } -- returns job_id immediately
|
|
131
|
+
agy_wait { job_id, wait_ms } -- loop until lifecycle == "finished"
|
|
132
|
+
agy_result { job_id, section } -- verdict, verification, response text
|
|
133
|
+
agy_logs { job_id } -- only if you want the stream itself
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`agy_start` with `dry_run: true` resolves configuration and policy without
|
|
137
|
+
spawning `agy`, so you can settle permissions before spending quota.
|
|
138
|
+
|
|
139
|
+
Need a job to see a toolchain that lives outside the workspace — `./gradlew`
|
|
140
|
+
reading `~/.jdks`, say? Add it to the project's own permission ceiling file
|
|
141
|
+
(`~/.agy-worker/projects/<hash>/policy.json`, `agy_capabilities.ceiling.path`
|
|
142
|
+
tells you the exact path), then ask for it in `agy_start`:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
// ~/.agy-worker/projects/<hash>/policy.json
|
|
146
|
+
{ "version": 2, "read_roots": ["~/.jdks"] }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
agy_start { profile: "general_worker", permissions: { read_roots: ["~/.jdks"] }, ... }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Without the matching ceiling entry, `read_roots` in the request is
|
|
154
|
+
dropped and reported in `rejected_read_roots` — see
|
|
155
|
+
[`docs/permissions.md`](./docs/permissions.md) for the full model.
|
|
156
|
+
|
|
157
|
+
When a job comes back `blocked`, `agy_result`'s `verification.blockers[]` says
|
|
158
|
+
who refused. Each entry carries `actionable` (can a different `agy_start` lift
|
|
159
|
+
it) and `remedy` (what to change — for our own gate, the rule string the
|
|
160
|
+
effective allow list was missing). `actionable: false` means no `agy_start`
|
|
161
|
+
argument will help: the command tried to leave the workspace, the rule is not
|
|
162
|
+
in the project ceiling, or the ceiling forces the sandbox on — each of those is
|
|
163
|
+
a human editing `policy.json`, or a different command.
|
|
164
|
+
|
|
165
|
+
## Tools
|
|
166
|
+
|
|
167
|
+
| Tool | Role |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `agy_start` | Start a job, return `job_id` immediately. |
|
|
170
|
+
| `agy_wait` | Long-poll until the job **finishes** or `wait_ms` runs out. Returns a compact judgement packet, not logs. |
|
|
171
|
+
| `agy_result` | Full, paged result: broker verdict, agent self-report, verification. |
|
|
172
|
+
| `agy_logs` | Raw or normalized event stream, by byte cursor or tail. |
|
|
173
|
+
| `agy_send` | Queue a follow-up turn on a session-mode job. Cannot interrupt a running turn. |
|
|
174
|
+
| `agy_cancel` | Kill a running job and its whole process group. |
|
|
175
|
+
| `agy_list_jobs` | Running and recently finished jobs in this project. |
|
|
176
|
+
| `agy_sessions` | List, inspect, or close `agy` conversations. |
|
|
177
|
+
| `agy_capabilities` | Models, profiles, the project ceiling as loaded, limits, discovered project root, server version. |
|
|
178
|
+
| `agy_ceiling` | Read-only: the ceiling, the effective policy, denial history, and a review of a draft ceiling. Never writes. |
|
|
179
|
+
|
|
180
|
+
Parameter-level detail, the `outcome` vocabulary, and the two "blocked" classes
|
|
181
|
+
are in [`docs/tools.md`](./docs/tools.md).
|
|
182
|
+
|
|
183
|
+
## Permissions
|
|
184
|
+
|
|
185
|
+
Every tool call `agy` makes passes through our own `PreToolUse` hook, which
|
|
186
|
+
decides allow/deny and OS-sandbox bypass on every single call — `agy`'s own
|
|
187
|
+
approval engine is disabled for every job (0.2.0), so our gate is the sole
|
|
188
|
+
authority. Three owners set the rules, each able to do one thing to the layer
|
|
189
|
+
below it: **code** (hard denies, the two shipped profiles — fixed), the
|
|
190
|
+
**project's own ceiling file** (`~/.agy-worker/projects/<hash>/policy.json`,
|
|
191
|
+
outside the workspace — widens what a job may ever ask for), and **the parent
|
|
192
|
+
agent's `agy_start.permissions`** (narrows within that ceiling, never widens
|
|
193
|
+
it).
|
|
194
|
+
|
|
195
|
+
Two profiles ship today:
|
|
196
|
+
|
|
197
|
+
- **`research_readonly`** (default) — read-only workspace access and shallow
|
|
198
|
+
`git` inspection. No writes, no interpreters.
|
|
199
|
+
- **`general_worker`** — read/write inside the workspace, `git`, `pytest`, and
|
|
200
|
+
the common build commands (`./gradlew`, `gradle`, `mvn`, `npm test`,
|
|
201
|
+
`npm run`, `javac`, `java`). `git push`, `curl`, package installs, `rm -rf`, and `sudo`
|
|
202
|
+
are denied by default — a project ceiling's `exceptions` can lift them, a
|
|
203
|
+
client request never can — and an action that
|
|
204
|
+
matches nothing is **denied**, never delegated to `agy`'s own engine — a
|
|
205
|
+
bound job's gate never answers "ask".
|
|
206
|
+
|
|
207
|
+
Client-requested permissions can only **narrow** the ceiling: `allow` is
|
|
208
|
+
intersected with it, `deny` always wins, `sandbox: "seatbelt" | "agy"` raises the OS
|
|
209
|
+
sandbox on for the job, and `read_roots` (extra `--add-dir` roots for a
|
|
210
|
+
toolchain outside the workspace) is intersected with the ceiling's own list.
|
|
211
|
+
|
|
212
|
+
The OS sandbox is **off** for allowed commands on `general_worker` (0.2.1) —
|
|
213
|
+
the gate's string match, not the kernel, is what bounds an allowed command,
|
|
214
|
+
same as Claude Code. It stays on for `research_readonly`, and a project can
|
|
215
|
+
force it on for every job with `sandbox: "agy"` in its ceiling file; expect
|
|
216
|
+
in-workspace builds, tests, and `git commit` to fail there on agy 1.1.24.
|
|
217
|
+
|
|
218
|
+
`agy_start` reports what the ceiling did to your request —
|
|
219
|
+
`policy_summary` (`allow_count`, `bypass_sandbox`, `sandbox_forced_by`) and a
|
|
220
|
+
`source: "policy_ceiling"` blocker per rejected rule. Watch for
|
|
221
|
+
`allow_count: 0`: a fully rejected `allow` request collapses the effective
|
|
222
|
+
list to empty and takes the profile's own defaults with it.
|
|
223
|
+
|
|
224
|
+
Full model — the three owners, the ceiling file schema, the gate's decision
|
|
225
|
+
order, containment, `verify_command`, and denial recovery — is in
|
|
226
|
+
[`docs/permissions.md`](./docs/permissions.md).
|
|
227
|
+
|
|
228
|
+
### Proposing a ceiling (skill)
|
|
229
|
+
|
|
230
|
+
The package ships a Claude Code skill, `skills/agy-ceiling/SKILL.md`, that
|
|
231
|
+
walks the parent agent through drafting a project ceiling from denial history
|
|
232
|
+
and the repository, validating it with `agy_ceiling`, and showing it to you —
|
|
233
|
+
and that forbids writing the file without your explicit approval of that
|
|
234
|
+
draft. The server has no code path that writes the ceiling at all; the skill is
|
|
235
|
+
the second wall.
|
|
236
|
+
|
|
237
|
+
For the case where *you* want the ceiling, not the agent, there is a slash
|
|
238
|
+
command, `commands/agy-ceiling.md`: `/agy-ceiling cargo build, git push` runs
|
|
239
|
+
the same procedure on demand, seeded with the commands you name, and still
|
|
240
|
+
stops for your answer before writing. `agy-worker-setup` installs both (see
|
|
241
|
+
[Install](#install)); by hand it is a copy:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
cp -R "$(npm root -g)/agy-worker-mcp/skills/agy-ceiling" .claude/skills/
|
|
245
|
+
cp "$(npm root -g)/agy-worker-mcp/commands/agy-ceiling.md" .claude/commands/
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Documentation
|
|
249
|
+
|
|
250
|
+
- [`docs/tools.md`](./docs/tools.md) — the ten tools, parameter by parameter,
|
|
251
|
+
and the result vocabulary
|
|
252
|
+
- [`docs/permissions.md`](./docs/permissions.md) — the three-owner permission
|
|
253
|
+
model, the ceiling file, the gate's decision order, containment,
|
|
254
|
+
`verify_command`, denial recovery
|
|
255
|
+
- [`docs/operations.md`](./docs/operations.md) — state layout, lifecycle, locks,
|
|
256
|
+
timeouts, retention, test suites
|
|
257
|
+
- [`CHANGELOG.md`](./CHANGELOG.md) — what changed in each version
|
|
258
|
+
|
|
259
|
+
## Development
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npm run typecheck # tsc --noEmit
|
|
263
|
+
npm test # vitest, against test/fake-agy — never the real agy binary
|
|
264
|
+
npm run build # emits dist/server.js, dist/runner.js, dist/gate.js, dist/setup.js
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`npm test` and CI run exclusively against the scripted fake in
|
|
268
|
+
[`test/fake-agy/`](./test/fake-agy); the real `agy` CLI is never invoked there,
|
|
269
|
+
since every invocation spends real quota. The real binary is exercised only by
|
|
270
|
+
the opt-in live suite:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
npm run test:live # spends real agy quota
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Trademarks and terms
|
|
277
|
+
|
|
278
|
+
agy-worker-mcp is an unofficial third-party integration; the name describes what
|
|
279
|
+
it drives, not who made it. Google, Antigravity, Gemini, and `agy` are Google LLC
|
|
280
|
+
trademarks; no license to those marks is granted or implied by this package.
|
|
281
|
+
|
|
282
|
+
This package launches the unmodified, officially installed `agy` binary as a
|
|
283
|
+
local subprocess on the user's own machine, using documented command-line flags
|
|
284
|
+
only (`--print`, `--output-format`, `--add-dir`, `--model`, `--effort`,
|
|
285
|
+
`--print-timeout`, `--dangerously-skip-permissions`) and the documented
|
|
286
|
+
`.agents/hooks.json` PreToolUse hook mechanism. Authentication stays entirely
|
|
287
|
+
inside `agy` (the user's own `agy` login); this package never reads, stores,
|
|
288
|
+
proxies, or forwards Antigravity credentials or tokens and never calls
|
|
289
|
+
Antigravity or Gemini backends itself. Model calls run inside agy's own harness,
|
|
290
|
+
and every job is charged to the user's own Antigravity plan quota. Multiple jobs
|
|
291
|
+
in parallel (limit `max_running_jobs`) consume quota faster than an interactive
|
|
292
|
+
session; keep parallelism modest.
|
|
293
|
+
|
|
294
|
+
Using this package means running agy under your own account. You are responsible
|
|
295
|
+
for complying with the [Google Antigravity Additional Terms of Service](https://antigravity.google/terms/)
|
|
296
|
+
and the [Antigravity FAQ on third-party tools](https://antigravity.google/docs/faq/).
|
|
297
|
+
Google's terms forbid using third-party software with an Antigravity login to
|
|
298
|
+
reach the models outside the official product, and Google has suspended accounts
|
|
299
|
+
for that. This package is designed to stay on the "spawn the official CLI" side
|
|
300
|
+
of that line, but the authors make no representation that Google agrees, and
|
|
301
|
+
Google may change its terms. Users needing a different harness should use a
|
|
302
|
+
Vertex AI or AI Studio API key as Google's FAQ suggests.
|
|
303
|
+
|
|
304
|
+
Provided "as is" under the [MIT license](#license); no warranty regarding
|
|
305
|
+
compliance with any third-party terms.
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Draft this project's agy-worker permission ceiling (policy.json) from denial history and the repo, review it with me, and write it only after I approve
|
|
3
|
+
argument-hint: [commands the jobs will need, e.g. "cargo build, make test, git push"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Draft the agy-worker-mcp project ceiling for this repository and write it only after I approve the draft you show me.
|
|
7
|
+
|
|
8
|
+
Context I am giving you: $ARGUMENTS
|
|
9
|
+
|
|
10
|
+
Do this, in order:
|
|
11
|
+
|
|
12
|
+
1. Call `agy_ceiling` with no arguments. Note `path`, whether the file is `present`, the current `ceiling`, the `effective` allow/deny/hard_deny lists, and `history.denied_rules` (what past jobs were refused, how often).
|
|
13
|
+
2. Read this repository to learn what a job here actually runs: build files, test runners, `package.json` scripts, `Makefile`, `Cargo.toml`, `build.gradle*`, `pyproject.toml`, CI config. Fold in the commands I listed above, if any.
|
|
14
|
+
3. Write a `version: 2` draft:
|
|
15
|
+
- `allow`: build/test/read commands the profile does not already allow (`agy_ceiling` marks duplicates).
|
|
16
|
+
- `exceptions`: only rules the profile denies that the task genuinely needs (`git push`, `npm install`, `curl`, …). One line of rationale each. Never `HARD_DENY` rules.
|
|
17
|
+
- `read_roots`: toolchains outside the workspace a job must read (`~/.gradle`, `~/.jdks`, …).
|
|
18
|
+
- `write_roots`: only if a sandboxed build must write outside the workspace.
|
|
19
|
+
- `sandbox`: leave `"none"` unless I asked for `"seatbelt"` or `"agy"`.
|
|
20
|
+
- Never set `command_policy: "denylist"` unless I explicitly asked for it.
|
|
21
|
+
4. Call `agy_ceiling({ draft, expected_commands })` with the draft and the commands from steps 1–2. Fix it until `review.ok` is true and every `expected_commands` entry you expect to run is `allow`. Keep the `review.rules[].risk` classes.
|
|
22
|
+
5. Show me the complete file as a fenced JSON block (a diff against the current file if one exists), with one line of rationale per rule and the risk class beside every `exceptions` entry and every `network` / `install` / `destructive` / `privilege` rule.
|
|
23
|
+
6. Ask me, in one line, whether to write it to `path`. Do not write before I answer. If I say yes, write exactly the shown JSON to that path and re-run `agy_ceiling()` to confirm it loads with no `warnings`. If I say no or change something, revise and show it again. If I want to write it myself, give me:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
cat > <path> <<'JSON'
|
|
27
|
+
<the approved JSON>
|
|
28
|
+
JSON
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Rules that do not bend: the server has no code path that writes this file, and neither do you without my in-conversation "yes" to the exact draft shown. `exceptions` and `command_policy: "denylist"` widen what an unattended job can do to the network, remote repositories, and the filesystem — call that out when you show them.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ArtifactCheck, Blocker, DenialClass1, DenialClass2, EffectivePolicy, GateLogEntry, PolicySummary } from '../contract/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a non-gate refusal came from agy's own permission engine.
|
|
4
|
+
*
|
|
5
|
+
* Substring match against measured wording, the same technique `detectClass2`
|
|
6
|
+
* uses on environment blocks and for the same reason: agy emits no structured
|
|
7
|
+
* field saying who refused.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isAgyEngineRefusal(message: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* A Class 1 event → the blocker it actually is.
|
|
12
|
+
*
|
|
13
|
+
* Three outcomes, because three different next actions: our gate refused
|
|
14
|
+
* (fixable with a rule), agy's own engine refused (nothing of ours applies), or
|
|
15
|
+
* a command simply failed (not a permission matter at all).
|
|
16
|
+
*/
|
|
17
|
+
export declare function blockerFromDenial(d: DenialClass1): Blocker;
|
|
18
|
+
/** A Class 2 signature match. Recovery depends on whether and why the job ran sandboxed. */
|
|
19
|
+
export declare function blockerFromEnvironmentBlock(b: DenialClass2, policy?: EffectivePolicy | null): Blocker;
|
|
20
|
+
/**
|
|
21
|
+
* I4: the runner's own gate watchdog
|
|
22
|
+
* killed the job because `jobs/<id>/gate-log.jsonl` never received a line
|
|
23
|
+
* within 3s of the first tool step — our PreToolUse hook never actually ran,
|
|
24
|
+
* so nothing before the kill was policy-checked at all. Never actionable — no
|
|
25
|
+
* `permissions.allow` entry fixes a hook that never loaded — and
|
|
26
|
+
* `decideOutcome` forces `outcome: 'process_error'` for this directly, not
|
|
27
|
+
* merely `blocked`, since `blocked` would imply the gate ran and refused.
|
|
28
|
+
*/
|
|
29
|
+
export declare function blockerFromGateMissing(): Blocker;
|
|
30
|
+
/** A broker-side check that failed. Today that is only a missing expected artifact. */
|
|
31
|
+
export declare function blockerFromMissingArtifact(a: ArtifactCheck): Blocker;
|
|
32
|
+
/**
|
|
33
|
+
* Build blockers from gate-log entries.
|
|
34
|
+
*
|
|
35
|
+
* When an entry carries `abort_reason: 'max_denials'`, emit an extra blocker
|
|
36
|
+
* reporting that the job was aborted after reaching the denial limit.
|
|
37
|
+
*/
|
|
38
|
+
export declare function blockersFromGateLog(log: string | readonly GateLogEntry[]): Blocker[];
|
|
39
|
+
/**
|
|
40
|
+
* The "no project ceiling" line for a finished job, or null. Said only when a
|
|
41
|
+
* gate blocker's remedy is a rule string — exactly what a ceiling `allow` /
|
|
42
|
+
* `exceptions` entry would open — and the policy recorded that no ceiling
|
|
43
|
+
* file existed. A foreign hook's denial (no rule), containment, unsupported
|
|
44
|
+
* tools and sandbox blocks are not the ceiling's business, so they stay quiet.
|
|
45
|
+
* Legacy job files without `ceiling_present` never trigger it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function ceilingAbsenceWarning(policy: EffectivePolicy | null | undefined, blockers: readonly Blocker[]): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Pre-flight blockers from a resolved policy: every `permissions.allow` entry
|
|
50
|
+
* the profile ceiling refused, every `permissions.read_roots` entry the
|
|
51
|
+
* human ceiling refused, plus the collapse case.
|
|
52
|
+
*
|
|
53
|
+
* `blocks_outcome` is false throughout — there is no job yet, so there is no
|
|
54
|
+
* outcome to block; these never reach `verification`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function policyCeilingBlockers(policy: EffectivePolicy): Blocker[];
|
|
57
|
+
/**
|
|
58
|
+
* One warning line per blocker. The only renderer — a caller must never have to
|
|
59
|
+
* infer "can I fix this" from prose wording.
|
|
60
|
+
*/
|
|
61
|
+
export declare function renderBlocker(b: Blocker): string;
|
|
62
|
+
export declare function renderBlockers(blockers: Blocker[]): string[];
|
|
63
|
+
/**
|
|
64
|
+
* `1 gate denial (actionable), 1 sandbox block` — grouped by source, in first
|
|
65
|
+
* appearance order, so a headline says who refused without listing everything.
|
|
66
|
+
*/
|
|
67
|
+
export declare function summarizeBlockers(blockers: Blocker[]): string;
|
|
68
|
+
export declare function countActionable(blockers: Blocker[]): number;
|
|
69
|
+
/** The single predicate `outcome` is decided by. */
|
|
70
|
+
export declare function hasOutcomeBlocker(blockers: Blocker[]): boolean;
|
|
71
|
+
/** What `agy_start` reports back about the policy it just resolved. */
|
|
72
|
+
export interface PolicyDescription {
|
|
73
|
+
policy_summary: PolicySummary;
|
|
74
|
+
blockers: Blocker[];
|
|
75
|
+
warnings: string[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Describe a resolved policy in the same vocabulary a finished job is judged
|
|
79
|
+
* in, for both the `dry_run` and the real `agy_start` reply.
|
|
80
|
+
*
|
|
81
|
+
* Built in one place so the two replies cannot drift: before 0.1.1 the real
|
|
82
|
+
* reply said nothing at all about permissions, and a caller whose
|
|
83
|
+
* `permissions.allow` had been rejected wholesale had no way to notice.
|
|
84
|
+
*/
|
|
85
|
+
export declare function describePolicy(policy: EffectivePolicy): PolicyDescription;
|
|
86
|
+
//# sourceMappingURL=blockers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockers.d.ts","sourceRoot":"","sources":["../../src/broker/blockers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,aAAa,EACb,OAAO,EAEP,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACd,MAAM,sBAAsB,CAAA;AA6C7B;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3D;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,YAAY,GAAG,OAAO,CA2D1D;AAED,4FAA4F;AAC5F,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CA6DrG;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAUhD;AAED,uFAAuF;AACvF,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAWpE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,YAAY,EAAE,GAAG,OAAO,EAAE,CAoCpF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAI7H;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CA8CxE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAMhD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAE5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAoB7D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAE3D;AAED,oDAAoD;AACpD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAE9D;AAED,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,aAAa,CAAA;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAgBzE"}
|