@smeltjs/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli/args.d.ts +12 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +61 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config.d.ts +22 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +32 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/hooks.d.ts +153 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +1180 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/init.d.ts +3 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +6 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/run.d.ts +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +19 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/hooks/guard-core.d.ts +164 -0
- package/dist/hooks/guard-core.d.ts.map +1 -0
- package/dist/hooks/guard-core.js +513 -0
- package/dist/hooks/guard-core.js.map +1 -0
- package/dist/hooks/shim.d.ts +85 -0
- package/dist/hooks/shim.d.ts.map +1 -0
- package/dist/hooks/shim.js +107 -0
- package/dist/hooks/shim.js.map +1 -0
- package/dist/hooks/shims/claude-code.d.ts +23 -0
- package/dist/hooks/shims/claude-code.d.ts.map +1 -0
- package/dist/hooks/shims/claude-code.js +61 -0
- package/dist/hooks/shims/claude-code.js.map +1 -0
- package/dist/hooks/shims/cline.d.ts +17 -0
- package/dist/hooks/shims/cline.d.ts.map +1 -0
- package/dist/hooks/shims/cline.js +39 -0
- package/dist/hooks/shims/cline.js.map +1 -0
- package/dist/hooks/shims/codex.d.ts +23 -0
- package/dist/hooks/shims/codex.d.ts.map +1 -0
- package/dist/hooks/shims/codex.js +56 -0
- package/dist/hooks/shims/codex.js.map +1 -0
- package/dist/hooks/shims/cursor.d.ts +19 -0
- package/dist/hooks/shims/cursor.d.ts.map +1 -0
- package/dist/hooks/shims/cursor.js +47 -0
- package/dist/hooks/shims/cursor.js.map +1 -0
- package/dist/hooks/shims/gemini.d.ts +23 -0
- package/dist/hooks/shims/gemini.d.ts.map +1 -0
- package/dist/hooks/shims/gemini.js +53 -0
- package/dist/hooks/shims/gemini.js.map +1 -0
- package/dist/hooks/shims/grok.d.ts +18 -0
- package/dist/hooks/shims/grok.d.ts.map +1 -0
- package/dist/hooks/shims/grok.js +37 -0
- package/dist/hooks/shims/grok.js.map +1 -0
- package/dist/hooks/shims/hermes.d.ts +22 -0
- package/dist/hooks/shims/hermes.d.ts.map +1 -0
- package/dist/hooks/shims/hermes.js +50 -0
- package/dist/hooks/shims/hermes.js.map +1 -0
- package/dist/net/policy.d.ts.map +1 -1
- package/dist/net/policy.js +1 -0
- package/dist/net/policy.js.map +1 -1
- package/package.json +12 -4
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { decide, isMainModule, parseGuardRequest, readAllOfStdin, readGuardSettings, } from './guard-core.js';
|
|
3
|
+
/** The pure half of the shim loop, for tests: raw harness input → rendered output. */
|
|
4
|
+
export function renderShimDecision(adapter, raw, settings, cwd, statFile) {
|
|
5
|
+
const request = adapter.toRequest(raw);
|
|
6
|
+
if (request === undefined)
|
|
7
|
+
return adapter.pass();
|
|
8
|
+
const effectiveCwd = adapter.cwd?.(raw) ?? cwd;
|
|
9
|
+
const decision = statFile === undefined
|
|
10
|
+
? decide(request, settings, effectiveCwd)
|
|
11
|
+
: decide(request, settings, effectiveCwd, statFile);
|
|
12
|
+
if (decision.action === 'allow')
|
|
13
|
+
return adapter.pass();
|
|
14
|
+
if (settings.enforcement === 'rewrite' &&
|
|
15
|
+
adapter.rewrite !== undefined &&
|
|
16
|
+
request.tool === 'Bash' &&
|
|
17
|
+
decision.suggestion !== undefined) {
|
|
18
|
+
return adapter.rewrite(raw, request, decision);
|
|
19
|
+
}
|
|
20
|
+
return adapter.deny(raw, request, decision);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The whole shim as a process: read the harness's stdin, decide, render, exit.
|
|
24
|
+
* Fail open exactly like the guard core — unparseable stdin passes with a stderr
|
|
25
|
+
* warning, and an unexpected error passes too. A shim crash must never read as a
|
|
26
|
+
* policy decision.
|
|
27
|
+
*/
|
|
28
|
+
export function runShimMain(adapter) {
|
|
29
|
+
let output;
|
|
30
|
+
try {
|
|
31
|
+
const text = readAllOfStdin();
|
|
32
|
+
let raw;
|
|
33
|
+
try {
|
|
34
|
+
raw = JSON.parse(text);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
process.stderr.write(`smelt shim: stdin was not JSON — allowing the call. Fix the hook wiring.\n`);
|
|
38
|
+
raw = undefined;
|
|
39
|
+
}
|
|
40
|
+
if (raw === undefined) {
|
|
41
|
+
output = adapter.pass();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const settings = readGuardSettings(process.cwd(), (text_) => process.stderr.write(`${text_}\n`));
|
|
45
|
+
output = renderShimDecision(adapter, raw, settings, process.cwd());
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
process.stderr.write(`smelt shim: unexpected error — allowing the call. ` +
|
|
50
|
+
`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`);
|
|
51
|
+
output = adapter.pass();
|
|
52
|
+
}
|
|
53
|
+
if (output.stderr !== undefined && output.stderr !== '')
|
|
54
|
+
process.stderr.write(output.stderr);
|
|
55
|
+
if (output.stdout !== '')
|
|
56
|
+
process.stdout.write(output.stdout);
|
|
57
|
+
process.exitCode = output.exitCode;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Map one `{tool name, tool input}` pair — the shape every surveyed harness feeds its
|
|
61
|
+
* pre-tool hook, under varying key spellings — onto a {@link GuardRequest}.
|
|
62
|
+
* `undefined` for tools the guard has no opinion on: pass through.
|
|
63
|
+
*/
|
|
64
|
+
export function toolCallRequest(toolName, toolInput, names) {
|
|
65
|
+
if (typeof toolName !== 'string')
|
|
66
|
+
return undefined;
|
|
67
|
+
const input = typeof toolInput === 'object' && toolInput !== null && !Array.isArray(toolInput)
|
|
68
|
+
? toolInput
|
|
69
|
+
: {};
|
|
70
|
+
if (names.read.includes(toolName)) {
|
|
71
|
+
const path = firstString(input, ['file_path', 'path', 'absolute_path', 'filePath']);
|
|
72
|
+
if (path === undefined)
|
|
73
|
+
return undefined;
|
|
74
|
+
const offsetLimited = firstDefined(input, ['offset', 'limit', 'start_line', 'end_line']) !== undefined;
|
|
75
|
+
return { tool: 'Read', input: { path, offsetLimited } };
|
|
76
|
+
}
|
|
77
|
+
if (names.bash.includes(toolName)) {
|
|
78
|
+
const command = firstString(input, ['command']);
|
|
79
|
+
if (command === undefined)
|
|
80
|
+
return undefined;
|
|
81
|
+
return { tool: 'Bash', input: { command } };
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
function firstString(input, keys) {
|
|
86
|
+
for (const key of keys) {
|
|
87
|
+
const value = input[key];
|
|
88
|
+
if (typeof value === 'string' && value !== '')
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
function firstDefined(input, keys) {
|
|
94
|
+
for (const key of keys) {
|
|
95
|
+
if (input[key] !== undefined && input[key] !== null)
|
|
96
|
+
return input[key];
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
/** The raw stdin as a keyed record, or an empty record when it is not object-shaped. */
|
|
101
|
+
export function asRecord(raw) {
|
|
102
|
+
return typeof raw === 'object' && raw !== null && !Array.isArray(raw)
|
|
103
|
+
? raw
|
|
104
|
+
: {};
|
|
105
|
+
}
|
|
106
|
+
export { isMainModule, parseGuardRequest };
|
|
107
|
+
//# sourceMappingURL=shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shim.js","sourceRoot":"","sources":["../../src/hooks/shim.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code shim — VERIFIED tier.
|
|
4
|
+
*
|
|
5
|
+
* Schema per <https://code.claude.com/docs/en/hooks> (verified 2026-09-02; the deep
|
|
6
|
+
* dive is docs/research/2026-09-02-agent-enforcement.md § 1):
|
|
7
|
+
*
|
|
8
|
+
* - stdin: `{ hook_event_name: "PreToolUse", tool_name, tool_input, cwd }`. For
|
|
9
|
+
* `Read`, `tool_input.file_path` is already absolute and `offset`/`limit` mark a
|
|
10
|
+
* windowed read; for `Bash`, `tool_input.command` is the full command string, and
|
|
11
|
+
* a relative path in it resolves against the payload's `cwd` — the *session's*
|
|
12
|
+
* working directory, which after the model `cd`s differs from this hook
|
|
13
|
+
* process's own cwd.
|
|
14
|
+
* - deny: `hookSpecificOutput.permissionDecision: "deny"` with
|
|
15
|
+
* `permissionDecisionReason` — which is **shown to the model**, so the guard's
|
|
16
|
+
* reason (the exact replacement command, the `smelt retrieve` contract) lands in
|
|
17
|
+
* the transcript as steering.
|
|
18
|
+
* - rewrite (opt-in, `hooks.enforcement: "rewrite"`): `updatedInput` replaces the
|
|
19
|
+
* entire input object of the *same* tool (v2.0.10+), so a Bash command can be
|
|
20
|
+
* substituted but a Read can never become a Bash call — Reads deny in every mode.
|
|
21
|
+
*/
|
|
22
|
+
export declare const adapter: ShimAdapter;
|
|
23
|
+
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/claude-code.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,OAAO,EAAE,WAqCrB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { asRecord, isMainModule, runShimMain, toolCallRequest } from '../shim.js';
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code shim — VERIFIED tier.
|
|
4
|
+
*
|
|
5
|
+
* Schema per <https://code.claude.com/docs/en/hooks> (verified 2026-09-02; the deep
|
|
6
|
+
* dive is docs/research/2026-09-02-agent-enforcement.md § 1):
|
|
7
|
+
*
|
|
8
|
+
* - stdin: `{ hook_event_name: "PreToolUse", tool_name, tool_input, cwd }`. For
|
|
9
|
+
* `Read`, `tool_input.file_path` is already absolute and `offset`/`limit` mark a
|
|
10
|
+
* windowed read; for `Bash`, `tool_input.command` is the full command string, and
|
|
11
|
+
* a relative path in it resolves against the payload's `cwd` — the *session's*
|
|
12
|
+
* working directory, which after the model `cd`s differs from this hook
|
|
13
|
+
* process's own cwd.
|
|
14
|
+
* - deny: `hookSpecificOutput.permissionDecision: "deny"` with
|
|
15
|
+
* `permissionDecisionReason` — which is **shown to the model**, so the guard's
|
|
16
|
+
* reason (the exact replacement command, the `smelt retrieve` contract) lands in
|
|
17
|
+
* the transcript as steering.
|
|
18
|
+
* - rewrite (opt-in, `hooks.enforcement: "rewrite"`): `updatedInput` replaces the
|
|
19
|
+
* entire input object of the *same* tool (v2.0.10+), so a Bash command can be
|
|
20
|
+
* substituted but a Read can never become a Bash call — Reads deny in every mode.
|
|
21
|
+
*/
|
|
22
|
+
export const adapter = {
|
|
23
|
+
toRequest: (raw) => {
|
|
24
|
+
const fields = asRecord(raw);
|
|
25
|
+
return toolCallRequest(fields['tool_name'], fields['tool_input'], {
|
|
26
|
+
read: ['Read'],
|
|
27
|
+
bash: ['Bash'],
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
cwd: (raw) => {
|
|
31
|
+
const cwd = asRecord(raw)['cwd'];
|
|
32
|
+
return typeof cwd === 'string' && cwd !== '' ? cwd : undefined;
|
|
33
|
+
},
|
|
34
|
+
pass: () => ({ stdout: '', exitCode: 0 }),
|
|
35
|
+
deny: (_raw, _request, decision) => ({
|
|
36
|
+
stdout: `${JSON.stringify({
|
|
37
|
+
hookSpecificOutput: {
|
|
38
|
+
hookEventName: 'PreToolUse',
|
|
39
|
+
permissionDecision: 'deny',
|
|
40
|
+
permissionDecisionReason: decision.reason ?? 'denied by the smelt guard',
|
|
41
|
+
},
|
|
42
|
+
})}\n`,
|
|
43
|
+
exitCode: 0,
|
|
44
|
+
}),
|
|
45
|
+
rewrite: (raw, _request, decision) => ({
|
|
46
|
+
stdout: `${JSON.stringify({
|
|
47
|
+
hookSpecificOutput: {
|
|
48
|
+
hookEventName: 'PreToolUse',
|
|
49
|
+
permissionDecision: 'allow',
|
|
50
|
+
permissionDecisionReason: `smelt rewrote this command (hooks.enforcement: "rewrite"): ` +
|
|
51
|
+
`${decision.suggestion ?? ''}`,
|
|
52
|
+
// updatedInput replaces the whole input object — unchanged fields ride along.
|
|
53
|
+
updatedInput: { ...asRecord(asRecord(raw)['tool_input']), command: decision.suggestion },
|
|
54
|
+
},
|
|
55
|
+
})}\n`,
|
|
56
|
+
exitCode: 0,
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
if (isMainModule(import.meta.url))
|
|
60
|
+
runShimMain(adapter);
|
|
61
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/hooks/shims/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE;YAChE,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,IAAI,EAAE,CAAC,MAAM,CAAC;SACf,CAAC,CAAC;IACL,CAAC;IACD,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE;QACX,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,CAAC;IACD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,MAAM;gBAC1B,wBAAwB,EAAE,QAAQ,CAAC,MAAM,IAAI,2BAA2B;aACzE;SACF,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,OAAO;gBAC3B,wBAAwB,EACtB,6DAA6D;oBAC7D,GAAG,QAAQ,CAAC,UAAU,IAAI,EAAE,EAAE;gBAChC,8EAA8E;gBAC9E,YAAY,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE;aACzF;SACF,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;CACH,CAAC;AAEF,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC;IAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Cline shim — EXPERIMENTAL tier: schema mapped from the capability matrix
|
|
4
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Cline row; primary source
|
|
5
|
+
* <https://docs.cline.bot/features/hooks>), not yet smoke-tested against the real
|
|
6
|
+
* binary.
|
|
7
|
+
*
|
|
8
|
+
* - event: `PreToolUse`, delivered to an executable under `.clinerules/hooks/`;
|
|
9
|
+
* stdin carries the tool call (accepted spellings: `tool_name`/`tool_input`,
|
|
10
|
+
* `toolName`/`toolInput`, or nested under `preToolUse`).
|
|
11
|
+
* - deny: `{ "cancel": true, "errorMessage": … }`. **Deny-only**: the response
|
|
12
|
+
* schema has no input-modification field, so under
|
|
13
|
+
* `hooks.enforcement: "rewrite"` this harness falls back to the deny, whose
|
|
14
|
+
* reason still carries the exact replacement pipeline.
|
|
15
|
+
*/
|
|
16
|
+
export declare const adapter: ShimAdapter;
|
|
17
|
+
//# sourceMappingURL=cline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cline.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/cline.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO,EAAE,WAqBrB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { asRecord, isMainModule, runShimMain, toolCallRequest } from '../shim.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cline shim — EXPERIMENTAL tier: schema mapped from the capability matrix
|
|
4
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Cline row; primary source
|
|
5
|
+
* <https://docs.cline.bot/features/hooks>), not yet smoke-tested against the real
|
|
6
|
+
* binary.
|
|
7
|
+
*
|
|
8
|
+
* - event: `PreToolUse`, delivered to an executable under `.clinerules/hooks/`;
|
|
9
|
+
* stdin carries the tool call (accepted spellings: `tool_name`/`tool_input`,
|
|
10
|
+
* `toolName`/`toolInput`, or nested under `preToolUse`).
|
|
11
|
+
* - deny: `{ "cancel": true, "errorMessage": … }`. **Deny-only**: the response
|
|
12
|
+
* schema has no input-modification field, so under
|
|
13
|
+
* `hooks.enforcement: "rewrite"` this harness falls back to the deny, whose
|
|
14
|
+
* reason still carries the exact replacement pipeline.
|
|
15
|
+
*/
|
|
16
|
+
export const adapter = {
|
|
17
|
+
toRequest: (raw) => {
|
|
18
|
+
const fields = asRecord(raw);
|
|
19
|
+
const nested = asRecord(fields['preToolUse']);
|
|
20
|
+
const name = fields['tool_name'] ?? fields['toolName'] ?? nested['toolName'] ?? nested['tool'];
|
|
21
|
+
const input = fields['tool_input'] ?? fields['toolInput'] ?? nested['toolInput'] ?? nested['input'];
|
|
22
|
+
return toolCallRequest(name, input, {
|
|
23
|
+
read: ['Read', 'read_file', 'readFile'],
|
|
24
|
+
bash: ['Bash', 'execute_command', 'executeCommand', 'shell'],
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
pass: () => ({ stdout: '', exitCode: 0 }),
|
|
28
|
+
deny: (_raw, _request, decision) => ({
|
|
29
|
+
stdout: `${JSON.stringify({
|
|
30
|
+
cancel: true,
|
|
31
|
+
errorMessage: decision.reason ?? 'denied by the smelt guard',
|
|
32
|
+
})}\n`,
|
|
33
|
+
exitCode: 0,
|
|
34
|
+
}),
|
|
35
|
+
// No `rewrite`: Cline's PreToolUse response cannot modify tool input (deny-only).
|
|
36
|
+
};
|
|
37
|
+
if (isMainModule(import.meta.url))
|
|
38
|
+
runShimMain(adapter);
|
|
39
|
+
//# sourceMappingURL=cline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cline.js","sourceRoot":"","sources":["../../../src/hooks/shims/cline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/F,MAAM,KAAK,GACT,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;QACxF,OAAO,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE;YAClC,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC;YACvC,IAAI,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,OAAO,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,MAAM,EAAE,IAAI;YACZ,YAAY,EAAE,QAAQ,CAAC,MAAM,IAAI,2BAA2B;SAC7D,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,kFAAkF;CACnF,CAAC;AAEF,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC;IAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Codex CLI shim — VERIFIED tier.
|
|
4
|
+
*
|
|
5
|
+
* Codex's experimental hooks (`features.hooks`, rust-v0.114.0+, on by default from
|
|
6
|
+
* 0.150.1) deliberately mirror Claude Code's schema — the survey is
|
|
7
|
+
* docs/research/2026-09-02-agent-enforcement.md § 3, primary source
|
|
8
|
+
* <https://developers.openai.com/codex/hooks>:
|
|
9
|
+
*
|
|
10
|
+
* - stdin: `{ tool_name, tool_input }`; shell input arrives as `tool_name: "Bash"`,
|
|
11
|
+
* `tool_input.command`.
|
|
12
|
+
* - deny: `hookSpecificOutput.permissionDecision: "deny"` + reason (or exit 2).
|
|
13
|
+
* - rewrite: `permissionDecision: "allow"` with `updatedInput` — for Bash,
|
|
14
|
+
* `updatedInput` **must include a string `command` field**, which this shim's
|
|
15
|
+
* rewrite always does.
|
|
16
|
+
*
|
|
17
|
+
* Two documented differences from Claude Code, both honoured here:
|
|
18
|
+
* `permissionDecision: "ask"` is NOT supported (a parse error — the tool proceeds),
|
|
19
|
+
* and this shim never emits it; project-layer hooks only run once the project is
|
|
20
|
+
* trusted, which the installer's output says out loud.
|
|
21
|
+
*/
|
|
22
|
+
export declare const adapter: ShimAdapter;
|
|
23
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/codex.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,OAAO,EAAE,WAgCrB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { asRecord, isMainModule, runShimMain, toolCallRequest } from '../shim.js';
|
|
2
|
+
/**
|
|
3
|
+
* Codex CLI shim — VERIFIED tier.
|
|
4
|
+
*
|
|
5
|
+
* Codex's experimental hooks (`features.hooks`, rust-v0.114.0+, on by default from
|
|
6
|
+
* 0.150.1) deliberately mirror Claude Code's schema — the survey is
|
|
7
|
+
* docs/research/2026-09-02-agent-enforcement.md § 3, primary source
|
|
8
|
+
* <https://developers.openai.com/codex/hooks>:
|
|
9
|
+
*
|
|
10
|
+
* - stdin: `{ tool_name, tool_input }`; shell input arrives as `tool_name: "Bash"`,
|
|
11
|
+
* `tool_input.command`.
|
|
12
|
+
* - deny: `hookSpecificOutput.permissionDecision: "deny"` + reason (or exit 2).
|
|
13
|
+
* - rewrite: `permissionDecision: "allow"` with `updatedInput` — for Bash,
|
|
14
|
+
* `updatedInput` **must include a string `command` field**, which this shim's
|
|
15
|
+
* rewrite always does.
|
|
16
|
+
*
|
|
17
|
+
* Two documented differences from Claude Code, both honoured here:
|
|
18
|
+
* `permissionDecision: "ask"` is NOT supported (a parse error — the tool proceeds),
|
|
19
|
+
* and this shim never emits it; project-layer hooks only run once the project is
|
|
20
|
+
* trusted, which the installer's output says out loud.
|
|
21
|
+
*/
|
|
22
|
+
export const adapter = {
|
|
23
|
+
toRequest: (raw) => {
|
|
24
|
+
const fields = asRecord(raw);
|
|
25
|
+
return toolCallRequest(fields['tool_name'], fields['tool_input'], {
|
|
26
|
+
read: ['Read'],
|
|
27
|
+
bash: ['Bash', 'shell'],
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
pass: () => ({ stdout: '', exitCode: 0 }),
|
|
31
|
+
deny: (_raw, _request, decision) => ({
|
|
32
|
+
stdout: `${JSON.stringify({
|
|
33
|
+
hookSpecificOutput: {
|
|
34
|
+
hookEventName: 'PreToolUse',
|
|
35
|
+
permissionDecision: 'deny',
|
|
36
|
+
permissionDecisionReason: decision.reason ?? 'denied by the smelt guard',
|
|
37
|
+
},
|
|
38
|
+
})}\n`,
|
|
39
|
+
exitCode: 0,
|
|
40
|
+
}),
|
|
41
|
+
rewrite: (raw, _request, decision) => ({
|
|
42
|
+
stdout: `${JSON.stringify({
|
|
43
|
+
hookSpecificOutput: {
|
|
44
|
+
hookEventName: 'PreToolUse',
|
|
45
|
+
permissionDecision: 'allow',
|
|
46
|
+
permissionDecisionReason: `smelt rewrote this command (hooks.enforcement: "rewrite"): ` +
|
|
47
|
+
`${decision.suggestion ?? ''}`,
|
|
48
|
+
updatedInput: { ...asRecord(asRecord(raw)['tool_input']), command: decision.suggestion },
|
|
49
|
+
},
|
|
50
|
+
})}\n`,
|
|
51
|
+
exitCode: 0,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
if (isMainModule(import.meta.url))
|
|
55
|
+
runShimMain(adapter);
|
|
56
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/hooks/shims/codex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE;YAChE,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;SACxB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,MAAM;gBAC1B,wBAAwB,EAAE,QAAQ,CAAC,MAAM,IAAI,2BAA2B;aACzE;SACF,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,OAAO;gBAC3B,wBAAwB,EACtB,6DAA6D;oBAC7D,GAAG,QAAQ,CAAC,UAAU,IAAI,EAAE,EAAE;gBAChC,YAAY,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE;aACzF;SACF,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;CACH,CAAC;AAEF,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC;IAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Cursor CLI/agent shim — EXPERIMENTAL tier: schema mapped from the capability
|
|
4
|
+
* matrix (docs/research/2026-09-02-harness-capability-matrix.md, Cursor row; primary
|
|
5
|
+
* source <https://cursor.com/docs/agent/hooks>), not yet smoke-tested against the
|
|
6
|
+
* real binary.
|
|
7
|
+
*
|
|
8
|
+
* - event: `preToolUse` (the broad hook — unlike `beforeShellExecution`, it
|
|
9
|
+
* supports input rewrite); stdin `{ tool_name, tool_input }`.
|
|
10
|
+
* - deny: `{ "permission": "deny", "agentMessage": … }` — `agentMessage` reaches
|
|
11
|
+
* the model, `userMessage` the human; the guard's steering text goes to the model.
|
|
12
|
+
* - rewrite: `{ "permission": "allow", "updated_input": { … } }` (snake_case, the
|
|
13
|
+
* whole input object).
|
|
14
|
+
*
|
|
15
|
+
* Cursor has no static permission-config file — gating is entirely hook code — so
|
|
16
|
+
* this shim plus the instruction file is the whole enforcement story there.
|
|
17
|
+
*/
|
|
18
|
+
export declare const adapter: ShimAdapter;
|
|
19
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO,EAAE,WA2BrB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { asRecord, isMainModule, runShimMain, toolCallRequest } from '../shim.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cursor CLI/agent shim — EXPERIMENTAL tier: schema mapped from the capability
|
|
4
|
+
* matrix (docs/research/2026-09-02-harness-capability-matrix.md, Cursor row; primary
|
|
5
|
+
* source <https://cursor.com/docs/agent/hooks>), not yet smoke-tested against the
|
|
6
|
+
* real binary.
|
|
7
|
+
*
|
|
8
|
+
* - event: `preToolUse` (the broad hook — unlike `beforeShellExecution`, it
|
|
9
|
+
* supports input rewrite); stdin `{ tool_name, tool_input }`.
|
|
10
|
+
* - deny: `{ "permission": "deny", "agentMessage": … }` — `agentMessage` reaches
|
|
11
|
+
* the model, `userMessage` the human; the guard's steering text goes to the model.
|
|
12
|
+
* - rewrite: `{ "permission": "allow", "updated_input": { … } }` (snake_case, the
|
|
13
|
+
* whole input object).
|
|
14
|
+
*
|
|
15
|
+
* Cursor has no static permission-config file — gating is entirely hook code — so
|
|
16
|
+
* this shim plus the instruction file is the whole enforcement story there.
|
|
17
|
+
*/
|
|
18
|
+
export const adapter = {
|
|
19
|
+
toRequest: (raw) => {
|
|
20
|
+
const fields = asRecord(raw);
|
|
21
|
+
return toolCallRequest(fields['tool_name'], fields['tool_input'], {
|
|
22
|
+
read: ['read_file', 'Read', 'ReadFile'],
|
|
23
|
+
bash: ['run_terminal_cmd', 'Shell', 'Bash', 'shell'],
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
pass: () => ({ stdout: '', exitCode: 0 }),
|
|
27
|
+
deny: (_raw, _request, decision) => ({
|
|
28
|
+
stdout: `${JSON.stringify({
|
|
29
|
+
permission: 'deny',
|
|
30
|
+
agentMessage: decision.reason ?? 'denied by the smelt guard',
|
|
31
|
+
})}\n`,
|
|
32
|
+
exitCode: 0,
|
|
33
|
+
}),
|
|
34
|
+
rewrite: (raw, _request, decision) => ({
|
|
35
|
+
stdout: `${JSON.stringify({
|
|
36
|
+
permission: 'allow',
|
|
37
|
+
updated_input: { ...asRecord(asRecord(raw)['tool_input']), command: decision.suggestion },
|
|
38
|
+
})}\n`,
|
|
39
|
+
// The allow/updated_input shape carries no message field — announce on stderr.
|
|
40
|
+
stderr: `smelt guard (rewrite mode): substituted the command in-flight with ` +
|
|
41
|
+
`\`${decision.suggestion ?? ''}\`. ${decision.reason ?? ''}\n`,
|
|
42
|
+
exitCode: 0,
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
if (isMainModule(import.meta.url))
|
|
46
|
+
runShimMain(adapter);
|
|
47
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/hooks/shims/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE;YAChE,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC;YACvC,IAAI,EAAE,CAAC,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;SACrD,CAAC,CAAC;IACL,CAAC;IACD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,QAAQ,CAAC,MAAM,IAAI,2BAA2B;SAC7D,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,UAAU,EAAE,OAAO;YACnB,aAAa,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE;SAC1F,CAAC,IAAI;QACN,+EAA+E;QAC/E,MAAM,EACJ,qEAAqE;YACrE,KAAK,QAAQ,CAAC,UAAU,IAAI,EAAE,OAAO,QAAQ,CAAC,MAAM,IAAI,EAAE,IAAI;QAChE,QAAQ,EAAE,CAAC;KACZ,CAAC;CACH,CAAC;AAEF,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC;IAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Gemini CLI shim — EXPERIMENTAL tier: the schema below is mapped from the
|
|
4
|
+
* capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Gemini
|
|
5
|
+
* CLI row; primary source <https://geminicli.com/docs/hooks/reference/>), and the
|
|
6
|
+
* field names were additionally checked 2026-09-02 against the shipped
|
|
7
|
+
* `@google/gemini-cli` 0.58.0 bundle (installed headlessly for KOT-212's
|
|
8
|
+
* verification pass): the BeforeTool stdin payload is `{ tool_name, tool_input }`,
|
|
9
|
+
* `read_file` takes `file_path` with `start_line`/`end_line` windows, the shell
|
|
10
|
+
* tool is `run_shell_command` with `command`, denial is `{ "decision": "deny",
|
|
11
|
+
* "reason": … }`, and `hookSpecificOutput.tool_input` substitutes the input. What
|
|
12
|
+
* has NOT run is a live end-to-end session (needs credentials this environment does
|
|
13
|
+
* not have), so the tier stays experimental — treat surprises as shim bugs and
|
|
14
|
+
* please report them.
|
|
15
|
+
*
|
|
16
|
+
* Carried caveat from the matrix: Gemini's policy engine has an open bug where an
|
|
17
|
+
* `allow` policy is ignored in non-interactive runs
|
|
18
|
+
* (<https://github.com/google-gemini/gemini-cli/issues/20469>) — this preset uses
|
|
19
|
+
* hooks, not the policy engine, but non-interactive behaviour is the least-tested
|
|
20
|
+
* corner; verify before relying on it in CI.
|
|
21
|
+
*/
|
|
22
|
+
export declare const adapter: ShimAdapter;
|
|
23
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/gemini.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,OAAO,EAAE,WA6BrB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { asRecord, isMainModule, runShimMain, toolCallRequest } from '../shim.js';
|
|
2
|
+
/**
|
|
3
|
+
* Gemini CLI shim — EXPERIMENTAL tier: the schema below is mapped from the
|
|
4
|
+
* capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Gemini
|
|
5
|
+
* CLI row; primary source <https://geminicli.com/docs/hooks/reference/>), and the
|
|
6
|
+
* field names were additionally checked 2026-09-02 against the shipped
|
|
7
|
+
* `@google/gemini-cli` 0.58.0 bundle (installed headlessly for KOT-212's
|
|
8
|
+
* verification pass): the BeforeTool stdin payload is `{ tool_name, tool_input }`,
|
|
9
|
+
* `read_file` takes `file_path` with `start_line`/`end_line` windows, the shell
|
|
10
|
+
* tool is `run_shell_command` with `command`, denial is `{ "decision": "deny",
|
|
11
|
+
* "reason": … }`, and `hookSpecificOutput.tool_input` substitutes the input. What
|
|
12
|
+
* has NOT run is a live end-to-end session (needs credentials this environment does
|
|
13
|
+
* not have), so the tier stays experimental — treat surprises as shim bugs and
|
|
14
|
+
* please report them.
|
|
15
|
+
*
|
|
16
|
+
* Carried caveat from the matrix: Gemini's policy engine has an open bug where an
|
|
17
|
+
* `allow` policy is ignored in non-interactive runs
|
|
18
|
+
* (<https://github.com/google-gemini/gemini-cli/issues/20469>) — this preset uses
|
|
19
|
+
* hooks, not the policy engine, but non-interactive behaviour is the least-tested
|
|
20
|
+
* corner; verify before relying on it in CI.
|
|
21
|
+
*/
|
|
22
|
+
export const adapter = {
|
|
23
|
+
toRequest: (raw) => {
|
|
24
|
+
const fields = asRecord(raw);
|
|
25
|
+
return toolCallRequest(fields['tool_name'], fields['tool_input'], {
|
|
26
|
+
read: ['read_file', 'ReadFile', 'Read'],
|
|
27
|
+
bash: ['run_shell_command', 'Shell', 'Bash'],
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
pass: () => ({ stdout: '', exitCode: 0 }),
|
|
31
|
+
deny: (_raw, _request, decision) => ({
|
|
32
|
+
stdout: `${JSON.stringify({
|
|
33
|
+
decision: 'deny',
|
|
34
|
+
reason: decision.reason ?? 'denied by the smelt guard',
|
|
35
|
+
})}\n`,
|
|
36
|
+
exitCode: 0,
|
|
37
|
+
}),
|
|
38
|
+
rewrite: (raw, _request, decision) => ({
|
|
39
|
+
stdout: `${JSON.stringify({
|
|
40
|
+
hookSpecificOutput: {
|
|
41
|
+
hookEventName: 'BeforeTool',
|
|
42
|
+
tool_input: { ...asRecord(asRecord(raw)['tool_input']), command: decision.suggestion },
|
|
43
|
+
},
|
|
44
|
+
})}\n`,
|
|
45
|
+
// The BeforeTool rewrite shape carries no reason field — announce on stderr.
|
|
46
|
+
stderr: `smelt guard (rewrite mode): substituted the command in-flight with ` +
|
|
47
|
+
`\`${decision.suggestion ?? ''}\`. ${decision.reason ?? ''}\n`,
|
|
48
|
+
exitCode: 0,
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
if (isMainModule(import.meta.url))
|
|
52
|
+
runShimMain(adapter);
|
|
53
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../src/hooks/shims/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE;YAChE,IAAI,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC;YACvC,IAAI,EAAE,CAAC,mBAAmB,EAAE,OAAO,EAAE,MAAM,CAAC;SAC7C,CAAC,CAAC;IACL,CAAC;IACD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,2BAA2B;SACvD,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,UAAU,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE;aACvF;SACF,CAAC,IAAI;QACN,6EAA6E;QAC7E,MAAM,EACJ,qEAAqE;YACrE,KAAK,QAAQ,CAAC,UAAU,IAAI,EAAE,OAAO,QAAQ,CAAC,MAAM,IAAI,EAAE,IAAI;QAChE,QAAQ,EAAE,CAAC;KACZ,CAAC;CACH,CAAC;AAEF,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC;IAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Grok CLI shim — EXPERIMENTAL tier: schema mapped from the capability matrix
|
|
4
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Grok CLI row; primary
|
|
5
|
+
* source <https://docs.x.ai/build/features/hooks>), not yet smoke-tested against
|
|
6
|
+
* the real binary. Note the matrix's provenance warning: the official CLI is
|
|
7
|
+
* `xai-org/grok-build` (binary `grok`) — `superagent-ai/grok-cli` is a third-party
|
|
8
|
+
* clone with a different hook surface.
|
|
9
|
+
*
|
|
10
|
+
* - event: `PreToolUse`; stdin `{ tool_name, tool_input }` (Claude-Code-like tool
|
|
11
|
+
* names).
|
|
12
|
+
* - deny: `{ "decision": "deny", "reason": … }` or exit 2. **Deny-only**: the input
|
|
13
|
+
* is read-only to hooks, so there is no rewrite here — under
|
|
14
|
+
* `hooks.enforcement: "rewrite"` this harness falls back to the deny, whose
|
|
15
|
+
* reason still carries the exact replacement pipeline.
|
|
16
|
+
*/
|
|
17
|
+
export declare const adapter: ShimAdapter;
|
|
18
|
+
//# sourceMappingURL=grok.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/grok.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,EAAE,WAiBrB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { asRecord, isMainModule, runShimMain, toolCallRequest } from '../shim.js';
|
|
2
|
+
/**
|
|
3
|
+
* Grok CLI shim — EXPERIMENTAL tier: schema mapped from the capability matrix
|
|
4
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Grok CLI row; primary
|
|
5
|
+
* source <https://docs.x.ai/build/features/hooks>), not yet smoke-tested against
|
|
6
|
+
* the real binary. Note the matrix's provenance warning: the official CLI is
|
|
7
|
+
* `xai-org/grok-build` (binary `grok`) — `superagent-ai/grok-cli` is a third-party
|
|
8
|
+
* clone with a different hook surface.
|
|
9
|
+
*
|
|
10
|
+
* - event: `PreToolUse`; stdin `{ tool_name, tool_input }` (Claude-Code-like tool
|
|
11
|
+
* names).
|
|
12
|
+
* - deny: `{ "decision": "deny", "reason": … }` or exit 2. **Deny-only**: the input
|
|
13
|
+
* is read-only to hooks, so there is no rewrite here — under
|
|
14
|
+
* `hooks.enforcement: "rewrite"` this harness falls back to the deny, whose
|
|
15
|
+
* reason still carries the exact replacement pipeline.
|
|
16
|
+
*/
|
|
17
|
+
export const adapter = {
|
|
18
|
+
toRequest: (raw) => {
|
|
19
|
+
const fields = asRecord(raw);
|
|
20
|
+
return toolCallRequest(fields['tool_name'], fields['tool_input'], {
|
|
21
|
+
read: ['Read', 'read_file', 'ReadFile'],
|
|
22
|
+
bash: ['Bash', 'shell', 'run_shell_command'],
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
pass: () => ({ stdout: '', exitCode: 0 }),
|
|
26
|
+
deny: (_raw, _request, decision) => ({
|
|
27
|
+
stdout: `${JSON.stringify({
|
|
28
|
+
decision: 'deny',
|
|
29
|
+
reason: decision.reason ?? 'denied by the smelt guard',
|
|
30
|
+
})}\n`,
|
|
31
|
+
exitCode: 0,
|
|
32
|
+
}),
|
|
33
|
+
// No `rewrite`: Grok's PreToolUse cannot modify tool input (deny-only harness).
|
|
34
|
+
};
|
|
35
|
+
if (isMainModule(import.meta.url))
|
|
36
|
+
runShimMain(adapter);
|
|
37
|
+
//# sourceMappingURL=grok.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok.js","sourceRoot":"","sources":["../../../src/hooks/shims/grok.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE;YAChE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC;YACvC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,mBAAmB,CAAC;SAC7C,CAAC,CAAC;IACL,CAAC;IACD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YACxB,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,2BAA2B;SACvD,CAAC,IAAI;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,gFAAgF;CACjF,CAAC;AAEF,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC;IAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ShimAdapter } from '../shim.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Hermes Agent shim — EXPERIMENTAL tier: schema mapped from the capability matrix
|
|
4
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Hermes Agent row; primary
|
|
5
|
+
* source <https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks>),
|
|
6
|
+
* not yet smoke-tested against the real binary.
|
|
7
|
+
*
|
|
8
|
+
* - event: `pre_tool_call`; stdin carries the tool name and its arguments
|
|
9
|
+
* (`tool_name` + `args`, with `tool_input` accepted as a fallback spelling).
|
|
10
|
+
* - deny: `{ "action": "block", "reason": … }`.
|
|
11
|
+
* - rewrite: `{ "action": "modify", "args": { … } }` — a **shallow merge** into the
|
|
12
|
+
* tool's arguments, so this shim sends only the `command` key and the harness
|
|
13
|
+
* keeps the rest. The modify schema has **no reason field**, so the substitution
|
|
14
|
+
* is announced on stderr — a rewrite must never be silent.
|
|
15
|
+
*
|
|
16
|
+
* Carried caveat from the matrix: Hermes has a known bypass where memory tools
|
|
17
|
+
* ignore `disabled_toolsets` (<https://github.com/NousResearch/hermes-agent/issues/46171>) —
|
|
18
|
+
* tool gating there is leaky at the edges, so treat this hook as best-effort
|
|
19
|
+
* steering, not a boundary.
|
|
20
|
+
*/
|
|
21
|
+
export declare const adapter: ShimAdapter;
|
|
22
|
+
//# sourceMappingURL=hermes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes.d.ts","sourceRoot":"","sources":["../../../src/hooks/shims/hermes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,OAAO,EAAE,WA2BrB,CAAC"}
|