approval-md 0.4.5 → 0.4.7
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/SPEC.md +24 -3
- package/dist/src/channels/contract.d.ts +15 -0
- package/dist/src/channels/contract.js +23 -1
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/telegram.d.ts +180 -33
- package/dist/src/channels/telegram.js +256 -87
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/cli/channel-telegram.d.ts +114 -35
- package/dist/src/cli/channel-telegram.js +259 -120
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/doctor.js +33 -13
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/help.d.ts +2 -0
- package/dist/src/cli/help.js +39 -3
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-exec-code.d.ts +150 -0
- package/dist/src/cli/hook-exec-code.js +357 -0
- package/dist/src/cli/hook-exec-code.js.map +1 -0
- package/dist/src/cli/hook.d.ts +154 -1
- package/dist/src/cli/hook.js +2344 -111
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/instructions.js +9 -6
- package/dist/src/cli/instructions.js.map +1 -1
- package/dist/src/cli/main.js +9 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/study.d.ts +22 -0
- package/dist/src/cli/study.js +153 -0
- package/dist/src/cli/study.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +14 -0
- package/dist/src/cli/verb-registry.js +37 -0
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/core/command-class.d.ts +349 -1
- package/dist/src/core/command-class.js +2347 -87
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/exec-code.d.ts +77 -0
- package/dist/src/core/exec-code.js +60 -0
- package/dist/src/core/exec-code.js.map +1 -0
- package/dist/src/core/gate.d.ts +38 -0
- package/dist/src/core/gate.js +26 -0
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/git-push-argv.d.ts +111 -0
- package/dist/src/core/git-push-argv.js +375 -0
- package/dist/src/core/git-push-argv.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +71 -30
- package/dist/src/core/harness-version.js +101 -34
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +8 -0
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/sender-identity.d.ts +13 -1
- package/dist/src/core/sender-identity.js +15 -0
- package/dist/src/core/sender-identity.js.map +1 -1
- package/dist/src/core/study-withdraw.d.ts +137 -0
- package/dist/src/core/study-withdraw.js +280 -0
- package/dist/src/core/study-withdraw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/README-extended.md +3 -2
- package/docs/cli-reference.md +83 -7
- package/package.json +2 -2
- package/schema/event.schema.json +213 -1
- package/schema/fixtures/event/invalid/approval-granted-study.json +23 -0
- package/schema/fixtures/event/invalid/approval-rejected-looks-wrong-cli-channel.json +23 -0
- package/schema/fixtures/event/invalid/approval-rejected-looks-wrong-operator.json +24 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-extra-key.json +24 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-unknown-reason.json +23 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-email-operator.json +20 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-no-operator.json +19 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-no-ref.json +19 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-wrong-actor.json +20 -0
- package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-wrong-channel.json +20 -0
- package/schema/fixtures/event/invalid/approval-revoked-study.json +20 -0
- package/schema/fixtures/event/valid/approval-rejected-looks-wrong.json +23 -0
- package/schema/fixtures/event/valid/approval-rejected-study-withdrawn.json +20 -0
- package/schema/fixtures/policy/invalid/exec-code-custody-boolean.json +11 -0
- package/schema/fixtures/policy/invalid/exec-code-execute-code-value.json +11 -0
- package/schema/fixtures/policy/invalid/exec-code-unknown-key.json +12 -0
- package/schema/fixtures/policy/valid/exec-code-both.json +17 -0
- package/schema/fixtures/policy/valid/exec-code-custody.json +11 -0
- package/schema/policy.schema.json +23 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `exec.code` gate: what the attested policy says about opaque agent code
|
|
3
|
+
* (APRV-510, 0.4.5 candidate).
|
|
4
|
+
*
|
|
5
|
+
* `python3 -c`, `node -e` and kin, an interpreter running a file that is not a
|
|
6
|
+
* skill script, and a Hermes `execute_code` call are all programs whose effect
|
|
7
|
+
* no classifier reads. Before this module every one of them was refused before
|
|
8
|
+
* policy (`hook-opaque`, `hook-unclassified`, `hook-hermes-execute-code-unbound`),
|
|
9
|
+
* and that stays the answer for every policy that does not speak: the gate is
|
|
10
|
+
* CLOSED unless the operator's policy opens it, key by key.
|
|
11
|
+
*
|
|
12
|
+
* Two keys, each opening one thing, and an absent key is the closed answer:
|
|
13
|
+
*
|
|
14
|
+
* - `exec.code.custody: attested` opens the class for the shell forms. It is the
|
|
15
|
+
* operator's statement that credential custody is in place: opaque code run by
|
|
16
|
+
* an agent that can read the tenant's credentials is a way round every gate
|
|
17
|
+
* that guards them, so the class may not exist without that statement.
|
|
18
|
+
* - `exec.code.execute_code: hooked`, beside the first, opens Hermes's
|
|
19
|
+
* `execute_code`. It is the operator's statement that a tool call made from
|
|
20
|
+
* inside the `execute_code` kernel re-fires the hook, which only a live probe
|
|
21
|
+
* can establish (`docs/hermes-hook.md`); without it one `execute_code` call
|
|
22
|
+
* would be an unbounded bypass of the adapter.
|
|
23
|
+
*
|
|
24
|
+
* Pure: a function of the parsed policy and nothing else. It decides whether a
|
|
25
|
+
* call may be CLASSIFIED `exec.code`; what the class then resolves to is the
|
|
26
|
+
* policy's ordinary class resolution (an `exec.code` row, an `exec.*` row, or
|
|
27
|
+
* `defaults.autonomy`), unchanged.
|
|
28
|
+
*/
|
|
29
|
+
import type { Policy } from "./policy-load.js";
|
|
30
|
+
/** The `exec` policy block (schema/policy.schema.json, APRV-510). */
|
|
31
|
+
export interface ExecBlock {
|
|
32
|
+
code?: {
|
|
33
|
+
custody?: "attested" | "unattested";
|
|
34
|
+
execute_code?: "hooked" | "unhooked";
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** The class an opened program is judged under. Mirrors `command-class.ts`. */
|
|
38
|
+
export declare const EXEC_CODE_POLICY_CLASS = "exec.code";
|
|
39
|
+
/** The policy line that opens the class, as a refusal names it. */
|
|
40
|
+
export declare const EXEC_CODE_CUSTODY_KEY = "exec.code.custody: attested";
|
|
41
|
+
/** The policy line that opens Hermes `execute_code`, as a refusal names it. */
|
|
42
|
+
export declare const EXEC_CODE_EXECUTE_KEY = "exec.code.execute_code: hooked";
|
|
43
|
+
/**
|
|
44
|
+
* Why the gate is closed, which decides the refusal's code and words.
|
|
45
|
+
*
|
|
46
|
+
* - `silent`: the policy says nothing about `exec.code` (no `exec` block and no
|
|
47
|
+
* `exec.code` class row), or there is no policy. The refusal is today's,
|
|
48
|
+
* byte for byte.
|
|
49
|
+
* - `unattested`: the policy names `exec.code` (an `exec` block, or a class row
|
|
50
|
+
* for it) without `custody: attested`. The refusal is
|
|
51
|
+
* `hook-exec-code-custody-unattested`, naming the key.
|
|
52
|
+
* - `window`: an open window is standing. A window suspends the policy's
|
|
53
|
+
* answer, and opening this class is a policy statement, so it is not reached
|
|
54
|
+
* through one; the refusal is today's.
|
|
55
|
+
* - `harness`: the adapter binds native bytes this class's payload cannot
|
|
56
|
+
* carry (Codex), so the class is not opened there; the refusal is today's.
|
|
57
|
+
*/
|
|
58
|
+
export type ExecCodeClosed = "silent" | "unattested" | "window" | "harness";
|
|
59
|
+
export interface ExecCodeGate {
|
|
60
|
+
/** May a shell segment be classified `exec.code`? */
|
|
61
|
+
shell: boolean;
|
|
62
|
+
/** May a Hermes `execute_code` call be classified `exec.code`? */
|
|
63
|
+
executeCode: boolean;
|
|
64
|
+
/** Why `shell` is false; `null` when it is true. */
|
|
65
|
+
closed: ExecCodeClosed | null;
|
|
66
|
+
}
|
|
67
|
+
/** The gate with everything closed for `reason`. */
|
|
68
|
+
export declare function closedExecCodeGate(reason: ExecCodeClosed): ExecCodeGate;
|
|
69
|
+
/**
|
|
70
|
+
* Does the policy name `exec.code` anywhere? An `exec` block, or a `classes`
|
|
71
|
+
* key that is exactly `exec.code`. A family row (`exec.*`) is not a mention:
|
|
72
|
+
* it was written for `exec.local`, and a policy that predates the class keeps
|
|
73
|
+
* its refusals byte for byte.
|
|
74
|
+
*/
|
|
75
|
+
export declare function execCodeMentioned(policy: Policy): boolean;
|
|
76
|
+
/** What `policy` opens. `null` (no policy loaded) opens nothing. */
|
|
77
|
+
export declare function execCodeGate(policy: Policy | null): ExecCodeGate;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `exec.code` gate: what the attested policy says about opaque agent code
|
|
3
|
+
* (APRV-510, 0.4.5 candidate).
|
|
4
|
+
*
|
|
5
|
+
* `python3 -c`, `node -e` and kin, an interpreter running a file that is not a
|
|
6
|
+
* skill script, and a Hermes `execute_code` call are all programs whose effect
|
|
7
|
+
* no classifier reads. Before this module every one of them was refused before
|
|
8
|
+
* policy (`hook-opaque`, `hook-unclassified`, `hook-hermes-execute-code-unbound`),
|
|
9
|
+
* and that stays the answer for every policy that does not speak: the gate is
|
|
10
|
+
* CLOSED unless the operator's policy opens it, key by key.
|
|
11
|
+
*
|
|
12
|
+
* Two keys, each opening one thing, and an absent key is the closed answer:
|
|
13
|
+
*
|
|
14
|
+
* - `exec.code.custody: attested` opens the class for the shell forms. It is the
|
|
15
|
+
* operator's statement that credential custody is in place: opaque code run by
|
|
16
|
+
* an agent that can read the tenant's credentials is a way round every gate
|
|
17
|
+
* that guards them, so the class may not exist without that statement.
|
|
18
|
+
* - `exec.code.execute_code: hooked`, beside the first, opens Hermes's
|
|
19
|
+
* `execute_code`. It is the operator's statement that a tool call made from
|
|
20
|
+
* inside the `execute_code` kernel re-fires the hook, which only a live probe
|
|
21
|
+
* can establish (`docs/hermes-hook.md`); without it one `execute_code` call
|
|
22
|
+
* would be an unbounded bypass of the adapter.
|
|
23
|
+
*
|
|
24
|
+
* Pure: a function of the parsed policy and nothing else. It decides whether a
|
|
25
|
+
* call may be CLASSIFIED `exec.code`; what the class then resolves to is the
|
|
26
|
+
* policy's ordinary class resolution (an `exec.code` row, an `exec.*` row, or
|
|
27
|
+
* `defaults.autonomy`), unchanged.
|
|
28
|
+
*/
|
|
29
|
+
/** The class an opened program is judged under. Mirrors `command-class.ts`. */
|
|
30
|
+
export const EXEC_CODE_POLICY_CLASS = "exec.code";
|
|
31
|
+
/** The policy line that opens the class, as a refusal names it. */
|
|
32
|
+
export const EXEC_CODE_CUSTODY_KEY = "exec.code.custody: attested";
|
|
33
|
+
/** The policy line that opens Hermes `execute_code`, as a refusal names it. */
|
|
34
|
+
export const EXEC_CODE_EXECUTE_KEY = "exec.code.execute_code: hooked";
|
|
35
|
+
/** The gate with everything closed for `reason`. */
|
|
36
|
+
export function closedExecCodeGate(reason) {
|
|
37
|
+
return { shell: false, executeCode: false, closed: reason };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Does the policy name `exec.code` anywhere? An `exec` block, or a `classes`
|
|
41
|
+
* key that is exactly `exec.code`. A family row (`exec.*`) is not a mention:
|
|
42
|
+
* it was written for `exec.local`, and a policy that predates the class keeps
|
|
43
|
+
* its refusals byte for byte.
|
|
44
|
+
*/
|
|
45
|
+
export function execCodeMentioned(policy) {
|
|
46
|
+
if (policy.exec !== undefined)
|
|
47
|
+
return true;
|
|
48
|
+
return Object.prototype.hasOwnProperty.call(policy.classes ?? {}, EXEC_CODE_POLICY_CLASS);
|
|
49
|
+
}
|
|
50
|
+
/** What `policy` opens. `null` (no policy loaded) opens nothing. */
|
|
51
|
+
export function execCodeGate(policy) {
|
|
52
|
+
if (policy === null)
|
|
53
|
+
return closedExecCodeGate("silent");
|
|
54
|
+
const code = policy.exec?.code;
|
|
55
|
+
if (code?.custody !== "attested") {
|
|
56
|
+
return closedExecCodeGate(execCodeMentioned(policy) ? "unattested" : "silent");
|
|
57
|
+
}
|
|
58
|
+
return { shell: true, executeCode: code.execute_code === "hooked", closed: null };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=exec-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-code.js","sourceRoot":"","sources":["../../../src/core/exec-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAYH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAElD,mEAAmE;AACnE,MAAM,CAAC,MAAM,qBAAqB,GAAG,6BAA6B,CAAC;AAEnE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,qBAAqB,GAAG,gCAAgC,CAAC;AA4BtE,oDAAoD;AACpD,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,sBAAsB,CAAC,CAAC;AAC5F,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,MAAqB;IAChD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAC/B,IAAI,IAAI,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,kBAAkB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACpF,CAAC"}
|
package/dist/src/core/gate.d.ts
CHANGED
|
@@ -1096,6 +1096,33 @@ export type RequestResult = {
|
|
|
1096
1096
|
* may have changed.
|
|
1097
1097
|
*/
|
|
1098
1098
|
export declare function request(logPath: string, input: RequestInput, actor: string, options?: GateOptions): RequestResult;
|
|
1099
|
+
/**
|
|
1100
|
+
* Why a study denied a request (amended SPEC.md §8), the closed vocabulary of
|
|
1101
|
+
* `payload.study.reason` on `approval.rejected`.
|
|
1102
|
+
*
|
|
1103
|
+
* - `study_withdrawn`: a hosted operator withdrew a still-pending request
|
|
1104
|
+
* before it could run. Written by the operator's withdraw verb alone, under
|
|
1105
|
+
* `human:study-team` on channel `study`; a channel may never pass it.
|
|
1106
|
+
* - `looks_wrong`: the resident flagged the card as a suspected study card and
|
|
1107
|
+
* skipped it. An ordinary resident deny with the flag recorded.
|
|
1108
|
+
*/
|
|
1109
|
+
export declare const STUDY_REASONS: readonly ["study_withdrawn", "looks_wrong"];
|
|
1110
|
+
export type StudyReason = (typeof STUDY_REASONS)[number];
|
|
1111
|
+
/**
|
|
1112
|
+
* The study record on a reject (amended SPEC.md §8), written as
|
|
1113
|
+
* `payload.study` on `approval.rejected` only.
|
|
1114
|
+
*
|
|
1115
|
+
* `operator` is an 8-hex pseudonymous id the operator's control plane derives
|
|
1116
|
+
* from the staff email under its own key (never the email), and `ref` is the withdraw's replay reference; both belong to
|
|
1117
|
+
* `study_withdrawn` and the schema refuses them on `looks_wrong`. A record of
|
|
1118
|
+
* why a person denied, never an input: no enforcement path reads it, and a
|
|
1119
|
+
* reader that ignores it reads an ordinary rejection.
|
|
1120
|
+
*/
|
|
1121
|
+
export interface DecisionStudy {
|
|
1122
|
+
reason: StudyReason;
|
|
1123
|
+
operator?: string;
|
|
1124
|
+
ref?: string;
|
|
1125
|
+
}
|
|
1099
1126
|
export interface DecideOptions extends GateOptions {
|
|
1100
1127
|
/** Free-text note recorded in the event payload (SPEC.md §8's example). */
|
|
1101
1128
|
note?: string;
|
|
@@ -1188,6 +1215,17 @@ export interface DecideOptions extends GateOptions {
|
|
|
1188
1215
|
* here or anywhere reads it back.
|
|
1189
1216
|
*/
|
|
1190
1217
|
rendering?: PromptRendering;
|
|
1218
|
+
/**
|
|
1219
|
+
* Why a study denied this request (amended SPEC.md §8), recorded as
|
|
1220
|
+
* `payload.study` on `approval.rejected` only.
|
|
1221
|
+
*
|
|
1222
|
+
* Written under the reject's own branch, the way `reaction` sits under the
|
|
1223
|
+
* grant's, so a value passed with `grant` or `revoke` is structurally unable
|
|
1224
|
+
* to reach a record. Copied field by field in a fixed shape, so nothing else
|
|
1225
|
+
* a caller hangs on the object reaches the log. A decision surface supplies
|
|
1226
|
+
* it; there is no CLI flag for it on `approval reject`.
|
|
1227
|
+
*/
|
|
1228
|
+
study?: DecisionStudy;
|
|
1191
1229
|
}
|
|
1192
1230
|
export type DecideResult = {
|
|
1193
1231
|
ok: true;
|
package/dist/src/core/gate.js
CHANGED
|
@@ -1901,6 +1901,20 @@ function attemptRequest(logPath, input, actor, options) {
|
|
|
1901
1901
|
...(live === null ? {} : { live }),
|
|
1902
1902
|
};
|
|
1903
1903
|
}
|
|
1904
|
+
// ---------------------------------------------------------------------------
|
|
1905
|
+
// decide
|
|
1906
|
+
// ---------------------------------------------------------------------------
|
|
1907
|
+
/**
|
|
1908
|
+
* Why a study denied a request (amended SPEC.md §8), the closed vocabulary of
|
|
1909
|
+
* `payload.study.reason` on `approval.rejected`.
|
|
1910
|
+
*
|
|
1911
|
+
* - `study_withdrawn`: a hosted operator withdrew a still-pending request
|
|
1912
|
+
* before it could run. Written by the operator's withdraw verb alone, under
|
|
1913
|
+
* `human:study-team` on channel `study`; a channel may never pass it.
|
|
1914
|
+
* - `looks_wrong`: the resident flagged the card as a suspected study card and
|
|
1915
|
+
* skipped it. An ordinary resident deny with the flag recorded.
|
|
1916
|
+
*/
|
|
1917
|
+
export const STUDY_REASONS = ["study_withdrawn", "looks_wrong"];
|
|
1904
1918
|
/**
|
|
1905
1919
|
* The two grades a grant may not carry without the approver's own words
|
|
1906
1920
|
* (amended SPEC.md §5.2). The same pair `core/audit.ts` enforces on a review,
|
|
@@ -2169,6 +2183,18 @@ function attemptDecide(logPath, actionKey, decision, actor, options) {
|
|
|
2169
2183
|
...(options.rendering.say_does_ignored === true ? { say_does_ignored: true } : {}),
|
|
2170
2184
|
};
|
|
2171
2185
|
}
|
|
2186
|
+
// Amended SPEC.md §8 (the study record). Reject only: a study may DENY a
|
|
2187
|
+
// pending request and never grant, alter or reopen one, so the field exists
|
|
2188
|
+
// on the one decision that denies. Copied field by field, `operator` and
|
|
2189
|
+
// `ref` only when present; the schema then holds each reason to its shape at
|
|
2190
|
+
// the write boundary.
|
|
2191
|
+
if (decision === "reject" && options.study !== undefined) {
|
|
2192
|
+
payload["study"] = {
|
|
2193
|
+
reason: options.study.reason,
|
|
2194
|
+
...(options.study.operator === undefined ? {} : { operator: options.study.operator }),
|
|
2195
|
+
...(options.study.ref === undefined ? {} : { ref: options.study.ref }),
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2172
2198
|
if (decision === "grant") {
|
|
2173
2199
|
const cls = derivation.declared.class ?? "";
|
|
2174
2200
|
const resolution = resolve(load, cls, derivation.declared.reversible === null ? {} : { reversible: derivation.declared.reversible });
|