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
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync, readdirSync, rmSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { readJsonIfExists, writeJsonAtomic } from '../contract/paths.js';
|
|
4
|
+
/**
|
|
5
|
+
* Single owner of `<workspace>/.agents/hooks.json`'s lifecycle: `start.ts` writes
|
|
6
|
+
* our entry when a job spawns, `reconcile.ts` removes it once no live job on that
|
|
7
|
+
* `cwd` needs it any more. Kept in one module — with the key constant — so the two
|
|
8
|
+
* call sites can never drift on what "our entry" means.
|
|
9
|
+
*/
|
|
10
|
+
/** The one key this package ever writes into `hooks.json`. Never touch any other key. */
|
|
11
|
+
export const GATE_HOOK_KEY = 'agy-worker-gate';
|
|
12
|
+
export function hooksFilePath(workspace) {
|
|
13
|
+
return join(workspace, '.agents', 'hooks.json');
|
|
14
|
+
}
|
|
15
|
+
/** Single-quote for a POSIX `sh -c` string, escaping any embedded `'`. */
|
|
16
|
+
function shQuote(s) {
|
|
17
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Write (merging, never clobbering unrelated keys) `<workspace>/.agents/hooks.json`
|
|
21
|
+
* so the PreToolUse gate loads for this workspace. `--add-dir` is what makes agy
|
|
22
|
+
* load it at all (see docs/permissions.md) — this is the piece that puts the
|
|
23
|
+
* file there in the first place, since nothing else in the package owns it.
|
|
24
|
+
*
|
|
25
|
+
* The command is just `node '<gatePath>'` — quoted (an unquoted absolute path
|
|
26
|
+
* breaks on any install path containing a space). There used to be a
|
|
27
|
+
* `|| printf '{"decision":"ask"}'` fallback here; it is gone. agy already fails closed on its own —
|
|
28
|
+
* empty/non-JSON stdout or a non-zero exit is a *denial*, not a pass-through
|
|
29
|
+
* (measured against 1.1.23, `src/gate/gate.ts`'s own header comment) — so the
|
|
30
|
+
* fallback never protected anything: its only effect was to convert that
|
|
31
|
+
* already-safe failure into `{"decision":"ask"}`, which in print mode reaches
|
|
32
|
+
* agy's built-in engine and lets a `run_command` call proceed with whatever
|
|
33
|
+
* `BypassSandbox` value the model itself asked for (M3-C) — the exact hole I2 exists to close.
|
|
34
|
+
* Losing the fallback also means `ensureGateHook` must only ever be called once
|
|
35
|
+
* `gatePath` is confirmed to exist (`agy_start`'s own `existsSync` check).
|
|
36
|
+
*
|
|
37
|
+
* Our key is written **first** in the object, ahead of every pre-existing key.
|
|
38
|
+
* Measured (M6): agy evaluates
|
|
39
|
+
* `hooks.json`'s PreToolUse groups in declaration order and a `deny` from an
|
|
40
|
+
* earlier group short-circuits the rest — a group installed ahead of ours that
|
|
41
|
+
* denies would mean our gate never runs at all for that call, and the runtime
|
|
42
|
+
* watchdog (`src/runner/gate-watchdog.ts`) would misread the resulting silence
|
|
43
|
+
* in `gate-log.jsonl` as "hooks never loaded". Writing ours first guarantees it
|
|
44
|
+
* runs on every tool call regardless of what else is declared in this file.
|
|
45
|
+
* (A hook group installed *outside* this file entirely — e.g. a user's global
|
|
46
|
+
* `~/.gemini/config/hooks.json` — that denies ahead of ours is the one case
|
|
47
|
+
* this cannot cover; the watchdog still reports it correctly if bluntly, as
|
|
48
|
+
* "gate never fired", which is safe — fail closed — just imprecise about why.
|
|
49
|
+
* Not handled here, by design.)
|
|
50
|
+
*/
|
|
51
|
+
export function ensureGateHook(workspace, gatePath) {
|
|
52
|
+
const hooksPath = hooksFilePath(workspace);
|
|
53
|
+
const existing = readJsonIfExists(hooksPath) ?? {};
|
|
54
|
+
const command = `node ${shQuote(gatePath)}`;
|
|
55
|
+
const ours = {
|
|
56
|
+
PreToolUse: [
|
|
57
|
+
{
|
|
58
|
+
matcher: '*',
|
|
59
|
+
hooks: [{ type: 'command', command, timeout: 15 }],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
delete existing[GATE_HOOK_KEY];
|
|
64
|
+
const merged = { [GATE_HOOK_KEY]: ours, ...existing };
|
|
65
|
+
writeJsonAtomic(hooksPath, merged);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Remove only {@link GATE_HOOK_KEY} from `<workspace>/.agents/hooks.json`,
|
|
69
|
+
* preserving every other key untouched. Deletes the file when it held nothing
|
|
70
|
+
* else, and the `.agents` directory when that leaves it empty. A no-op when the
|
|
71
|
+
* file does not exist or never carried our key.
|
|
72
|
+
*
|
|
73
|
+
* Best effort by design: called from
|
|
74
|
+
* `reconcile`, which must never fail a tool call over hook-file housekeeping. A
|
|
75
|
+
* stale entry left behind by a failure here is harmless until the next
|
|
76
|
+
* `agy_start` on the same workspace, which overwrites it unconditionally.
|
|
77
|
+
*/
|
|
78
|
+
export function removeGateHook(workspace) {
|
|
79
|
+
try {
|
|
80
|
+
const hooksPath = hooksFilePath(workspace);
|
|
81
|
+
const existing = readJsonIfExists(hooksPath);
|
|
82
|
+
if (!existing || !(GATE_HOOK_KEY in existing))
|
|
83
|
+
return;
|
|
84
|
+
delete existing[GATE_HOOK_KEY];
|
|
85
|
+
if (Object.keys(existing).length > 0) {
|
|
86
|
+
writeJsonAtomic(hooksPath, existing);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
rmSync(hooksPath, { force: true });
|
|
90
|
+
const agentsDir = join(workspace, '.agents');
|
|
91
|
+
try {
|
|
92
|
+
if (existsSync(agentsDir) && readdirSync(agentsDir).length === 0) {
|
|
93
|
+
rmSync(agentsDir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Best effort — an unreadable/already-gone directory is not a failure.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Best effort — reconcile must never break over hook-file housekeeping.
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=hooks-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-file.js","sourceRoot":"","sources":["../../src/gate/hooks-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAExE;;;;;GAKG;AAEH,yFAAyF;AACzF,MAAM,CAAC,MAAM,aAAa,GAAG,iBAAiB,CAAA;AAE9C,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,OAAO,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAA;AACjD,CAAC;AAED,0EAA0E;AAC1E,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAA;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,QAAgB;IAChE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,gBAAgB,CAA0B,SAAS,CAAC,IAAI,EAAE,CAAA;IAC3E,MAAM,OAAO,GAAG,QAAQ,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC3C,MAAM,IAAI,GAAG;QACX,UAAU,EAAE;YACV;gBACE,OAAO,EAAE,GAAG;gBACZ,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;aACnD;SACF;KACF,CAAA;IACD,OAAO,QAAQ,CAAC,aAAa,CAAC,CAAA;IAC9B,MAAM,MAAM,GAA4B,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC9E,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;AACpC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;QAC1C,MAAM,QAAQ,GAAG,gBAAgB,CAA0B,SAAS,CAAC,CAAA;QACrE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,aAAa,IAAI,QAAQ,CAAC;YAAE,OAAM;QAErD,OAAO,QAAQ,CAAC,aAAa,CAAC,CAAA;QAE9B,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;YACpC,OAAM;QACR,CAAC;QAED,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjE,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACrD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;QACzE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;IAC1E,CAAC;AACH,CAAC"}
|
package/dist/gate.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":""}
|
package/dist/gate.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Entry point for the PreToolUse hook. Compiles to `dist/gate.js`, the path
|
|
4
|
+
* written into `<workspace>/.agents/hooks.json`.
|
|
5
|
+
*
|
|
6
|
+
* ⚠ Never exit without printing a decision. `{}` and empty output are treated
|
|
7
|
+
* as denials by agy, and this process also runs for the user's own interactive agy
|
|
8
|
+
* sessions whenever a conversation cannot be matched to one of our jobs.
|
|
9
|
+
*/
|
|
10
|
+
import { PASSTHROUGH, emit, guardStdout, main } from './gate/gate.js';
|
|
11
|
+
// Before anything else can print. A hook that exits 0 with unparseable stdout
|
|
12
|
+
// is treated as a denial by agy, and this process always exits 0.
|
|
13
|
+
guardStdout();
|
|
14
|
+
main()
|
|
15
|
+
.then((code) => process.exit(code))
|
|
16
|
+
.catch(() => {
|
|
17
|
+
emit(PASSTHROUGH);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=gate.js.map
|
package/dist/gate.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAErE,8EAA8E;AAC9E,kEAAkE;AAClE,WAAW,EAAE,CAAA;AAEb,IAAI,EAAE;KACH,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KAClC,KAAK,CAAC,GAAG,EAAE;IACV,IAAI,CAAC,WAAW,CAAC,CAAA;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CeilingDraftReview, CeilingRuleRisk } from '../contract/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Risk class of one rule string. Order matters: the most consequential class
|
|
4
|
+
* wins when a pattern could be read two ways (`git push --force` is
|
|
5
|
+
* destructive before it is vcs_remote; `sudo npm install` is privilege before
|
|
6
|
+
* install). `filesystem` covers every non-command verb — the containment
|
|
7
|
+
* roots, not the rule list, bound those.
|
|
8
|
+
*/
|
|
9
|
+
export declare function classifyRuleRisk(rule: string): CeilingRuleRisk;
|
|
10
|
+
/** Rules a human should read twice before approving. */
|
|
11
|
+
export declare const ELEVATED_RISK: ReadonlySet<CeilingRuleRisk>;
|
|
12
|
+
/**
|
|
13
|
+
* Validate and annotate a draft `policy.json`.
|
|
14
|
+
*
|
|
15
|
+
* `errors` are exactly what `loadCeiling` would refuse (so `ok: true` means
|
|
16
|
+
* the file would load). `warnings` are advisory: redundant rules, exceptions
|
|
17
|
+
* that lift nothing, `denylist` mode. `rules` carries one row per rule with
|
|
18
|
+
* its risk class, so the parent agent can put the class next to every line
|
|
19
|
+
* it shows the user.
|
|
20
|
+
*/
|
|
21
|
+
export declare function reviewCeilingDraft(draft: unknown): CeilingDraftReview;
|
|
22
|
+
//# sourceMappingURL=ceiling-review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceiling-review.d.ts","sourceRoot":"","sources":["../../src/policy/ceiling-review.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAqB,MAAM,sBAAsB,CAAA;AA0DlG;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CA0B9D;AAED,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,eAAe,CAErD,CAAA;AAEF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAgDrE"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { ValidationError } from '../contract/errors.js';
|
|
2
|
+
import { parseCeilingJson } from './ceiling.js';
|
|
3
|
+
import { HARD_DENY } from './hard-deny.js';
|
|
4
|
+
import { getProfile } from './profiles.js';
|
|
5
|
+
import { parseRule } from './rules.js';
|
|
6
|
+
/**
|
|
7
|
+
* Review a ceiling draft for `agy_ceiling` (0.3.0 PR3). Deterministic
|
|
8
|
+
* and file-free: the same checks `loadCeiling` runs,
|
|
9
|
+
* plus the advisory layer a human wants before approving the file — what is
|
|
10
|
+
* redundant, what is being lifted, and how risky each rule is.
|
|
11
|
+
*
|
|
12
|
+
* The server never writes the ceiling. This module only tells the parent
|
|
13
|
+
* agent what a human is about to be asked to approve.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Every concrete command a `command(...)` pattern names, lower-cased. `|` is
|
|
17
|
+
* per-token alternation in `rules.ts` (`git status|log|diff` is three git
|
|
18
|
+
* subcommands, not `git status` or a bare `log`), so expand it token by token.
|
|
19
|
+
*/
|
|
20
|
+
function commandHeads(pattern) {
|
|
21
|
+
const tokens = pattern.trim().toLowerCase().split(/\s+/);
|
|
22
|
+
let heads = [''];
|
|
23
|
+
for (const token of tokens) {
|
|
24
|
+
const alternatives = token.split('|');
|
|
25
|
+
heads = heads.flatMap((h) => alternatives.map((a) => (h ? `${h} ${a}` : a)));
|
|
26
|
+
}
|
|
27
|
+
return heads;
|
|
28
|
+
}
|
|
29
|
+
const READ_UTILITY = new Set([
|
|
30
|
+
'ls', 'cat', 'head', 'tail', 'wc', 'grep', 'rg', 'find', 'stat', 'sed', 'sed -n', 'sort', 'uniq', 'diff',
|
|
31
|
+
'cut', 'tr', 'xargs', 'basename', 'dirname', 'realpath', 'which', 'echo', 'printf', 'test', 'true', 'false',
|
|
32
|
+
'pwd', 'env', 'date', 'uname', 'file', 'less', 'more', 'tree', 'jq', 'awk',
|
|
33
|
+
]);
|
|
34
|
+
const BUILD_HEADS = [
|
|
35
|
+
'gradle', './gradlew', 'gradlew', 'mvn', './mvnw', 'make', 'cmake', 'ninja', 'cargo', 'go', 'npm', 'npx', 'pnpm', 'yarn',
|
|
36
|
+
'node', 'node -e', 'python', 'python3', 'python -c', 'python3 -c', 'pytest', 'javac', 'java', 'tsc', 'vitest', 'jest',
|
|
37
|
+
'dotnet', 'swift', 'rustc', 'gcc', 'clang', 'bash -c', 'sh -c', 'bash', 'sh', 'zsh',
|
|
38
|
+
];
|
|
39
|
+
const NETWORK_HEADS = ['curl', 'wget', 'ssh', 'scp', 'sftp', 'nc', 'ncat', 'telnet', 'rsync', 'ping', 'dig', 'nslookup'];
|
|
40
|
+
const VCS_REMOTE_HEADS = ['git push', 'git fetch', 'git pull', 'git clone', 'git remote', 'git submodule', 'gh'];
|
|
41
|
+
const INSTALL_HEADS = [
|
|
42
|
+
'npm install', 'npm i', 'npm ci', 'npm add', 'pnpm install', 'pnpm add', 'yarn add', 'yarn install', 'pip install',
|
|
43
|
+
'pip3 install', 'brew', 'apt', 'apt-get', 'yum', 'dnf', 'cargo install', 'go install', 'gem install',
|
|
44
|
+
];
|
|
45
|
+
const DESTRUCTIVE_HEADS = [
|
|
46
|
+
'rm -rf', 'rm -r', 'git reset --hard', 'git clean', 'git filter-branch', 'git branch -d', 'git stash drop',
|
|
47
|
+
'git checkout -- .', 'git restore .', 'git restore --staged .', 'git push --force', 'git push -f', 'mkfs', 'dd',
|
|
48
|
+
'chmod -r', 'chown -r', 'kill', 'killall', 'pkill',
|
|
49
|
+
];
|
|
50
|
+
const PRIVILEGE_HEADS = ['sudo', 'doas', 'su', 'docker', 'podman', 'launchctl', 'systemctl'];
|
|
51
|
+
function startsWithAny(head, list) {
|
|
52
|
+
return list.some((h) => head === h || head.startsWith(`${h} `));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Risk class of one rule string. Order matters: the most consequential class
|
|
56
|
+
* wins when a pattern could be read two ways (`git push --force` is
|
|
57
|
+
* destructive before it is vcs_remote; `sudo npm install` is privilege before
|
|
58
|
+
* install). `filesystem` covers every non-command verb — the containment
|
|
59
|
+
* roots, not the rule list, bound those.
|
|
60
|
+
*/
|
|
61
|
+
export function classifyRuleRisk(rule) {
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = parseRule(rule);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return 'other';
|
|
68
|
+
}
|
|
69
|
+
if (parsed.verb !== 'command')
|
|
70
|
+
return parsed.verb === 'fetch' || parsed.verb === 'url' ? 'network' : 'filesystem';
|
|
71
|
+
if (parsed.regex || parsed.pattern === '*')
|
|
72
|
+
return 'other';
|
|
73
|
+
const heads = commandHeads(parsed.pattern);
|
|
74
|
+
const classes = heads.map((head) => {
|
|
75
|
+
if (startsWithAny(head, PRIVILEGE_HEADS))
|
|
76
|
+
return 'privilege';
|
|
77
|
+
if (startsWithAny(head, DESTRUCTIVE_HEADS))
|
|
78
|
+
return 'destructive';
|
|
79
|
+
if (startsWithAny(head, INSTALL_HEADS))
|
|
80
|
+
return 'install';
|
|
81
|
+
if (startsWithAny(head, NETWORK_HEADS))
|
|
82
|
+
return 'network';
|
|
83
|
+
if (startsWithAny(head, VCS_REMOTE_HEADS))
|
|
84
|
+
return 'vcs_remote';
|
|
85
|
+
if (head.startsWith('git ') || head === 'git')
|
|
86
|
+
return 'vcs_local';
|
|
87
|
+
if (READ_UTILITY.has(head) || startsWithAny(head, [...READ_UTILITY]))
|
|
88
|
+
return 'read_utility';
|
|
89
|
+
if (startsWithAny(head, BUILD_HEADS))
|
|
90
|
+
return 'build';
|
|
91
|
+
return 'other';
|
|
92
|
+
});
|
|
93
|
+
const order = [
|
|
94
|
+
'privilege', 'destructive', 'install', 'network', 'vcs_remote', 'other', 'build', 'vcs_local', 'read_utility', 'filesystem',
|
|
95
|
+
];
|
|
96
|
+
for (const c of order)
|
|
97
|
+
if (classes.includes(c))
|
|
98
|
+
return c;
|
|
99
|
+
return 'other';
|
|
100
|
+
}
|
|
101
|
+
/** Rules a human should read twice before approving. */
|
|
102
|
+
export const ELEVATED_RISK = new Set([
|
|
103
|
+
'privilege', 'destructive', 'install', 'network', 'vcs_remote',
|
|
104
|
+
]);
|
|
105
|
+
/**
|
|
106
|
+
* Validate and annotate a draft `policy.json`.
|
|
107
|
+
*
|
|
108
|
+
* `errors` are exactly what `loadCeiling` would refuse (so `ok: true` means
|
|
109
|
+
* the file would load). `warnings` are advisory: redundant rules, exceptions
|
|
110
|
+
* that lift nothing, `denylist` mode. `rules` carries one row per rule with
|
|
111
|
+
* its risk class, so the parent agent can put the class next to every line
|
|
112
|
+
* it shows the user.
|
|
113
|
+
*/
|
|
114
|
+
export function reviewCeilingDraft(draft) {
|
|
115
|
+
const errors = [];
|
|
116
|
+
let ceiling = null;
|
|
117
|
+
try {
|
|
118
|
+
ceiling = parseCeilingJson(draft, '<draft>');
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
errors.push(e instanceof ValidationError ? e.message : String(e));
|
|
122
|
+
}
|
|
123
|
+
const warnings = [];
|
|
124
|
+
const rules = [];
|
|
125
|
+
if (ceiling) {
|
|
126
|
+
warnings.push(...ceiling.warnings);
|
|
127
|
+
const profile = getProfile('general_worker');
|
|
128
|
+
const profileAllow = new Set(profile.allow);
|
|
129
|
+
const profileDeny = new Set(profile.deny);
|
|
130
|
+
const hardDeny = new Set(HARD_DENY);
|
|
131
|
+
for (const rule of ceiling.allow) {
|
|
132
|
+
const notes = [];
|
|
133
|
+
if (profileAllow.has(rule))
|
|
134
|
+
notes.push('already in the general_worker allow list');
|
|
135
|
+
if (profileDeny.has(rule))
|
|
136
|
+
notes.push('general_worker denies this; allow does not override deny — use exceptions');
|
|
137
|
+
if (hardDeny.has(rule))
|
|
138
|
+
notes.push('HARD_DENY; no file can allow this');
|
|
139
|
+
rules.push({ key: 'allow', rule, risk: classifyRuleRisk(rule), notes });
|
|
140
|
+
}
|
|
141
|
+
for (const rule of ceiling.deny) {
|
|
142
|
+
const notes = [];
|
|
143
|
+
if (profileDeny.has(rule))
|
|
144
|
+
notes.push('already denied by general_worker');
|
|
145
|
+
if (hardDeny.has(rule))
|
|
146
|
+
notes.push('already on HARD_DENY');
|
|
147
|
+
if (ceiling.exceptions.includes(rule))
|
|
148
|
+
notes.push('also listed in exceptions; deny wins, net effect denied');
|
|
149
|
+
rules.push({ key: 'deny', rule, risk: classifyRuleRisk(rule), notes });
|
|
150
|
+
}
|
|
151
|
+
for (const rule of ceiling.exceptions) {
|
|
152
|
+
const notes = [];
|
|
153
|
+
if (!profileDeny.has(rule))
|
|
154
|
+
notes.push('not on the general_worker deny list; lifts nothing (exact-string match)');
|
|
155
|
+
const risk = classifyRuleRisk(rule);
|
|
156
|
+
if (ELEVATED_RISK.has(risk))
|
|
157
|
+
notes.push(`${risk}: needs explicit line-by-line approval`);
|
|
158
|
+
rules.push({ key: 'exceptions', rule, risk, notes });
|
|
159
|
+
}
|
|
160
|
+
for (const r of rules) {
|
|
161
|
+
for (const n of r.notes)
|
|
162
|
+
warnings.push(`${r.key}: ${r.rule} — ${n}`);
|
|
163
|
+
}
|
|
164
|
+
if (ceiling.command_policy === 'denylist') {
|
|
165
|
+
warnings.push('command_policy: denylist lets every command not on a deny list run; needs explicit approval');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { ok: errors.length === 0, errors, warnings, rules };
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=ceiling-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceiling-review.js","sourceRoot":"","sources":["../../src/policy/ceiling-review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { CeilingSummary, SandboxMode } from '../contract/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The human-owned permission ceiling (see docs/permissions.md
|
|
4
|
+
* — the middle row of the three-owner table: code / **human** / parent agent).
|
|
5
|
+
*
|
|
6
|
+
* Lives at `<project state dir>/policy.json` — `projectPaths(root).dir` from
|
|
7
|
+
* `contract/paths.ts`, i.e. `<AGY_WORKER_HOME or ~/.agy-worker>/projects/<hash>/`,
|
|
8
|
+
* the very directory that already holds `project.json` and `index.db`. That
|
|
9
|
+
* directory sits outside the workspace by construction: it is keyed by
|
|
10
|
+
* `sha256(canonical_root)` under the user's home (or `AGY_WORKER_HOME`), never
|
|
11
|
+
* under the project's own tree. This is what makes it an actual ceiling — I6
|
|
12
|
+
* is exactly the invariant that a bound job
|
|
13
|
+
* can write anywhere `{workspace}/**` a `write_file` rule permits, so a
|
|
14
|
+
* ceiling file living inside the workspace could rewrite its own limits. Never
|
|
15
|
+
* move this file, or look it up, anywhere under a job's `cwd`.
|
|
16
|
+
*/
|
|
17
|
+
export interface Ceiling {
|
|
18
|
+
/** Whether a policy.json was actually found. False = the profile as shipped. */
|
|
19
|
+
present: boolean;
|
|
20
|
+
/** Absolute path the ceiling was (or would be) read from; null for a draft or the frozen empty ceiling. */
|
|
21
|
+
path: string | null;
|
|
22
|
+
/** Rules added to every profile's own allow ceiling (`{workspace}` left unsubstituted). */
|
|
23
|
+
allow: string[];
|
|
24
|
+
/** Rules added to every profile's own deny list (`{workspace}` left unsubstituted). */
|
|
25
|
+
deny: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Profile deny rules this project lifts (0.3.0). Matched by exact string
|
|
28
|
+
* against the profile's `deny` in `resolvePolicy`; a rule on `HARD_DENY`
|
|
29
|
+
* here is a file error, never a silent no-op.
|
|
30
|
+
*/
|
|
31
|
+
exceptions: string[];
|
|
32
|
+
/**
|
|
33
|
+
* OS boundary for general_worker commands (0.3.0 PR6): `none` (default,
|
|
34
|
+
* the 0.2.1 behaviour), `seatbelt` (our write-only `sandbox-exec` profile),
|
|
35
|
+
* or `agy` (agy's own sandbox, the 0.2.0 behaviour). A v2 `sandboxed: true`
|
|
36
|
+
* and every v1 `sandboxed: true` read as `agy`.
|
|
37
|
+
*/
|
|
38
|
+
sandbox: SandboxMode;
|
|
39
|
+
/**
|
|
40
|
+
* Read/exec roots outside the workspace (`--add-dir`). `~`-expanded,
|
|
41
|
+
* best-effort canonicalized glob patterns (`*`, `**` allowed, `rules.ts`'s
|
|
42
|
+
* own glob semantics — `matchesPathGlob`). Not job-specific, so resolved
|
|
43
|
+
* once here rather than deferred like the rule strings above.
|
|
44
|
+
*/
|
|
45
|
+
read_roots: string[];
|
|
46
|
+
/**
|
|
47
|
+
* Directories outside the workspace a job may write to. Canonical absolute
|
|
48
|
+
* paths (no globs — a write root is a directory, not a pattern). Extends
|
|
49
|
+
* `write_roots` for containment and the seatbelt profile alike.
|
|
50
|
+
*/
|
|
51
|
+
write_roots: string[];
|
|
52
|
+
/** Command evaluation mode: "allowlist" (default) or "denylist" (0.2.2 PR3). */
|
|
53
|
+
command_policy: 'allowlist' | 'denylist';
|
|
54
|
+
/** Schema version the file was written in; null when no file. */
|
|
55
|
+
version: 1 | 2 | null;
|
|
56
|
+
/** Non-fatal notes for `agy_capabilities` (e.g. v1 key names). */
|
|
57
|
+
warnings: string[];
|
|
58
|
+
}
|
|
59
|
+
export declare const EMPTY_CEILING: Ceiling;
|
|
60
|
+
/** `<project state dir>/policy.json`. Exported so callers (capabilities, tests) don't hardcode the filename. */
|
|
61
|
+
export declare function ceilingPath(paths: {
|
|
62
|
+
dir: string;
|
|
63
|
+
}): string;
|
|
64
|
+
/**
|
|
65
|
+
* Convert a ceiling `read_roots` pattern (potentially a glob like `~/.jdks/**`)
|
|
66
|
+
* into a concrete directory root for `--add-dir`.
|
|
67
|
+
*
|
|
68
|
+
* Takes the fixed prefix before the first wildcard (`*`), strips trailing `/`,
|
|
69
|
+
* resolves home expansion and canonicalization via `resolveDirPattern`, and returns
|
|
70
|
+
* the canonical directory path if it exists and is a directory; otherwise null.
|
|
71
|
+
*/
|
|
72
|
+
export declare function additionalDirRoot(pattern: string): string | null;
|
|
73
|
+
/**
|
|
74
|
+
* Load and validate the project ceiling.
|
|
75
|
+
*
|
|
76
|
+
* Missing file → {@link EMPTY_CEILING} (a project with no ceiling file grants
|
|
77
|
+
* nothing beyond the profiles' own defaults — narrower, not wider, than
|
|
78
|
+
* having one). Present but invalid — bad JSON, `version` other than `1`/`2`, a
|
|
79
|
+
* non-string entry, an entry that does not parse as `verb(pattern)`
|
|
80
|
+
* (`parseRule`), or a legacy `unsandboxed` entry — throws
|
|
81
|
+
* {@link ValidationError} with `field: 'ceiling'` and the file path, so
|
|
82
|
+
* `agy_start` fails closed and tells the human what to fix instead of quietly
|
|
83
|
+
* falling back to an empty ceiling (which would look like "nothing is
|
|
84
|
+
* misconfigured" when something very much is).
|
|
85
|
+
*
|
|
86
|
+
* `{workspace}` substitution on `allow`/`deny`/`exceptions` is
|
|
87
|
+
* deferred to `resolvePolicy` — this file has no workspace of its own; one
|
|
88
|
+
* project can run jobs against several `cwd` values under the same ceiling.
|
|
89
|
+
* `read_roots` entries are not job-specific, so they are `~`-expanded and
|
|
90
|
+
* canonicalized once, here.
|
|
91
|
+
*/
|
|
92
|
+
export declare function loadCeiling(paths: {
|
|
93
|
+
dir: string;
|
|
94
|
+
}): Ceiling;
|
|
95
|
+
/**
|
|
96
|
+
* The validation half of {@link loadCeiling}, separated so `agy_ceiling` can
|
|
97
|
+
* check a draft that exists only in the caller's hands — same rules, same
|
|
98
|
+
* error texts, no file. `path` only labels the error.
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseCeilingJson(json: unknown, path: string): Ceiling;
|
|
101
|
+
/**
|
|
102
|
+
* Is `canonicalDir` (already `~`-expanded/canonicalized, e.g. via
|
|
103
|
+
* {@link resolveDirPattern}) covered by some glob in the ceiling's
|
|
104
|
+
* `read_roots`? The one place `read_roots` matching happens, so
|
|
105
|
+
* `resolvePolicy` and any test never re-implement the glob semantics.
|
|
106
|
+
*/
|
|
107
|
+
export declare function additionalDirCovered(ceiling: Ceiling, canonicalDir: string): boolean;
|
|
108
|
+
/** `~`-expand and canonicalize a requested `read_roots` entry the same way ceiling entries are resolved. */
|
|
109
|
+
export declare function resolveRequestedDir(pattern: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* Render a loaded ceiling for `agy_capabilities`.
|
|
112
|
+
*
|
|
113
|
+
* No `{workspace}` substitution: capabilities has no per-job workspace (a
|
|
114
|
+
* project can run jobs against several `cwd` values), so a rule string
|
|
115
|
+
* carrying the literal `{workspace}` placeholder is returned exactly as
|
|
116
|
+
* written in `policy.json`. A caller sees the substituted, job-real form in
|
|
117
|
+
* `agy_start`'s own `policy_summary` instead.
|
|
118
|
+
*/
|
|
119
|
+
/**
|
|
120
|
+
* The one sentence every surface says when there is no ceiling file. Not an
|
|
121
|
+
* error — the profile as shipped is the safe default — but the agent and the
|
|
122
|
+
* user should both know that the door exists and who holds the key: the user
|
|
123
|
+
* approves, the agent (via `agy_ceiling` / the `agy-ceiling` skill) or the
|
|
124
|
+
* user (via `/agy-ceiling`) drafts.
|
|
125
|
+
*/
|
|
126
|
+
export declare function ceilingAbsenceHint(path: string | null): string;
|
|
127
|
+
export declare function describeCeiling(ceiling: Ceiling, path: string, present: boolean): CeilingSummary;
|
|
128
|
+
//# sourceMappingURL=ceiling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceiling.d.ts","sourceRoot":"","sources":["../../src/policy/ceiling.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAIvE;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,OAAO;IACtB,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAA;IAChB,2GAA2G;IAC3G,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,2FAA2F;IAC3F,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,uFAAuF;IACvF,IAAI,EAAE,MAAM,EAAE,CAAA;IACd;;;;OAIG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB;;;;;OAKG;IACH,OAAO,EAAE,WAAW,CAAA;IACpB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB;;;;OAIG;IACH,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,gFAAgF;IAChF,cAAc,EAAE,WAAW,GAAG,UAAU,CAAA;IACxC,iEAAiE;IACjE,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;IACrB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,OAY1B,CAAA;AAiCF,gHAAgH;AAChH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE1D;AAoBD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBhE;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAmB3D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAqFrE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAEpF;AAED,4GAA4G;AAC5G,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;;GAQG;AACH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAQ9D;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc,CAchG"}
|